Compare commits
No commits in common. "631bcdd29092d2da0420b687eb9d006f6b36224d" and "bfb06be4cf6c66d10cef4391097f4a4a0f8e20ac" have entirely different histories.
631bcdd290
...
bfb06be4cf
|
|
@ -1,16 +0,0 @@
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Models;
|
|
||||||
|
|
||||||
namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Components
|
|
||||||
{
|
|
||||||
[ViewComponent(Name = "PartnersGridComponent")]
|
|
||||||
public class PartnersGridComponent : ViewComponent
|
|
||||||
{
|
|
||||||
public async Task<IViewComponentResult> InvokeAsync(TestGridModel model)
|
|
||||||
{
|
|
||||||
// Here you can fetch data for this grid if needed
|
|
||||||
// For demo, just pass the model
|
|
||||||
return View(model.ViewComponentName +".cshtml", model);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,16 +0,0 @@
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Models;
|
|
||||||
|
|
||||||
namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Components
|
|
||||||
{
|
|
||||||
[ViewComponent(Name = "ShippingDocumentGridComponent")]
|
|
||||||
public class ShippingDocumentGridComponent : ViewComponent
|
|
||||||
{
|
|
||||||
public async Task<IViewComponentResult> InvokeAsync(TestGridModel model)
|
|
||||||
{
|
|
||||||
// Here you can fetch data for this grid if needed
|
|
||||||
// For demo, just pass the model
|
|
||||||
return View(model.ViewComponentName +".cshtml", model);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,16 +0,0 @@
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Models;
|
|
||||||
|
|
||||||
namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Components
|
|
||||||
{
|
|
||||||
[ViewComponent(Name = "ShippingGridComponent")]
|
|
||||||
public class ShippingGridComponent : ViewComponent
|
|
||||||
{
|
|
||||||
public async Task<IViewComponentResult> InvokeAsync(TestGridModel model)
|
|
||||||
{
|
|
||||||
// Here you can fetch data for this grid if needed
|
|
||||||
// For demo, just pass the model
|
|
||||||
return View(model.ViewComponentName +".cshtml", model);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,25 +0,0 @@
|
||||||
@model Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Models.ShippingDocumentListModel
|
|
||||||
@using Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Models
|
|
||||||
@using FruitBank.Common.Entities;
|
|
||||||
@using DevExtreme.AspNet.Mvc
|
|
||||||
<h4>Id: @Model.ShippingId</h4>
|
|
||||||
|
|
||||||
@(Html.DevExtreme().DataGrid<ShippingDocument>()
|
|
||||||
.ID("documentsGrid")
|
|
||||||
.DataSource(Model.ShippingDocumentList)
|
|
||||||
.KeyExpr("Id")
|
|
||||||
.ShowBorders(true)
|
|
||||||
.Editing(editing => {
|
|
||||||
editing.Mode(GridEditMode.Row);
|
|
||||||
editing.AllowUpdating(true);
|
|
||||||
editing.AllowAdding(false);
|
|
||||||
editing.AllowDeleting(true);
|
|
||||||
})
|
|
||||||
.Columns(c => {
|
|
||||||
c.AddFor(m => m.DocumentDate).Caption("Date").DataType(GridDataType.Date);
|
|
||||||
c.AddFor(m => m.SenderName).Caption("Sender");
|
|
||||||
c.AddFor(m => m.InvoiceNumber).Caption("Invoice #");
|
|
||||||
c.AddFor(m => m.TotalAmount).Caption("Amount").DataType(GridDataType.Number);
|
|
||||||
c.AddFor(m => m.ItemCount).Caption("ItemCount").DataType(GridDataType.Number);
|
|
||||||
})
|
|
||||||
)
|
|
||||||
|
|
@ -29,8 +29,6 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Controllers
|
||||||
{
|
{
|
||||||
ApiKey = _settings.ApiKey,
|
ApiKey = _settings.ApiKey,
|
||||||
ModelName = _settings.ModelName,
|
ModelName = _settings.ModelName,
|
||||||
OpenAIApiKey = _settings.OpenAIApiKey,
|
|
||||||
OpenAIModelName = _settings.OpenAIModelName,
|
|
||||||
IsEnabled = _settings.IsEnabled,
|
IsEnabled = _settings.IsEnabled,
|
||||||
ApiBaseUrl = _settings.ApiBaseUrl,
|
ApiBaseUrl = _settings.ApiBaseUrl,
|
||||||
MaxTokens = _settings.MaxTokens,
|
MaxTokens = _settings.MaxTokens,
|
||||||
|
|
@ -51,11 +49,8 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Controllers
|
||||||
// Map model properties to settings
|
// Map model properties to settings
|
||||||
_settings.ApiKey = model.ApiKey ?? string.Empty;
|
_settings.ApiKey = model.ApiKey ?? string.Empty;
|
||||||
_settings.ModelName = model.ModelName ?? string.Empty;
|
_settings.ModelName = model.ModelName ?? string.Empty;
|
||||||
_settings.OpenAIApiKey = model.OpenAIApiKey ?? string.Empty;
|
|
||||||
_settings.OpenAIModelName = model.OpenAIModelName ?? string.Empty;
|
|
||||||
_settings.IsEnabled = model.IsEnabled;
|
_settings.IsEnabled = model.IsEnabled;
|
||||||
_settings.ApiBaseUrl = model.ApiBaseUrl ?? string.Empty;
|
_settings.ApiBaseUrl = model.ApiBaseUrl ?? string.Empty;
|
||||||
_settings.OpenAIApiBaseUrl = model.OpenAIApiBaseUrl ?? string.Empty;
|
|
||||||
_settings.MaxTokens = model.MaxTokens;
|
_settings.MaxTokens = model.MaxTokens;
|
||||||
_settings.Temperature = model.Temperature;
|
_settings.Temperature = model.Temperature;
|
||||||
_settings.RequestTimeoutSeconds = model.RequestTimeoutSeconds;
|
_settings.RequestTimeoutSeconds = model.RequestTimeoutSeconds;
|
||||||
|
|
|
||||||
|
|
@ -1,322 +0,0 @@
|
||||||
using FruitBank.Common.Entities;
|
|
||||||
using Microsoft.AspNetCore.Http;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Microsoft.Extensions.Azure;
|
|
||||||
using Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Models;
|
|
||||||
using Nop.Plugin.Misc.FruitBankPlugin.Domains.DataLayer;
|
|
||||||
using Nop.Plugin.Misc.FruitBankPlugin.Services;
|
|
||||||
using Nop.Services.Security;
|
|
||||||
using Nop.Web.Areas.Admin.Controllers;
|
|
||||||
using Nop.Web.Framework;
|
|
||||||
using Nop.Web.Framework.Mvc.Filters;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers
|
|
||||||
{
|
|
||||||
[Area(AreaNames.ADMIN)]
|
|
||||||
[AuthorizeAdmin]
|
|
||||||
public class ManagementPageController : BaseAdminController
|
|
||||||
{
|
|
||||||
private readonly IPermissionService _permissionService;
|
|
||||||
protected readonly FruitBankDbContext _dbContext;
|
|
||||||
protected readonly AICalculationService _aiCalculationService;
|
|
||||||
|
|
||||||
public ManagementPageController(IPermissionService permissionService, FruitBankDbContext fruitBankDbContext, AICalculationService aiCalculationService)
|
|
||||||
{
|
|
||||||
_permissionService = permissionService;
|
|
||||||
_dbContext = fruitBankDbContext;
|
|
||||||
_aiCalculationService = aiCalculationService;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<IActionResult> Test()
|
|
||||||
{
|
|
||||||
if (!await _permissionService.AuthorizeAsync(StandardPermission.Security.ACCESS_ADMIN_PANEL))
|
|
||||||
return AccessDeniedView();
|
|
||||||
var testPageModel = new TestPageModel();
|
|
||||||
testPageModel.Grids = new List<TestGridModel>();
|
|
||||||
|
|
||||||
var testGridModel2 = new TestGridModel();
|
|
||||||
testGridModel2.GridName = "Orders";
|
|
||||||
testGridModel2.ViewComponentName = "ShippingDocumentGridComponent";
|
|
||||||
testPageModel.Grids.Add(testGridModel2);
|
|
||||||
|
|
||||||
var testGridModel = new TestGridModel();
|
|
||||||
testGridModel.GridName = "Shipping";
|
|
||||||
testGridModel.ViewComponentName = "ShippingGridComponent";
|
|
||||||
testPageModel.Grids.Add(testGridModel);
|
|
||||||
|
|
||||||
var testGridModel3 = new TestGridModel();
|
|
||||||
testGridModel3.GridName = "Partners";
|
|
||||||
testGridModel3.ViewComponentName = "PartnersGridComponent";
|
|
||||||
testPageModel.Grids.Add(testGridModel3);
|
|
||||||
|
|
||||||
//testGridModel.ViewComponentLocation = "~/Plugins/Misc.FruitBankPlugin/Areas/Admin/Components/Views/_ShippingGridComponent.cshtml";
|
|
||||||
return View("~/Plugins/Misc.FruitBankPlugin/Areas/Admin/Views/Order/Test.cshtml", testPageModel);
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet]
|
|
||||||
public async Task<IActionResult> GetShippings()
|
|
||||||
{
|
|
||||||
if (!await _permissionService.AuthorizeAsync(StandardPermission.Security.ACCESS_ADMIN_PANEL))
|
|
||||||
return AccessDeniedView();
|
|
||||||
|
|
||||||
// Mock data for now
|
|
||||||
var model = _dbContext.Shippings.GetAll(true).OrderByDescending(s => s.Created).ToList();
|
|
||||||
var valami = model;
|
|
||||||
//model. = await _dbContext.GetShippingDocumentsByShippingIdAsync(shippingId);
|
|
||||||
return Json(model);
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet]
|
|
||||||
public async Task<IActionResult> GetShippingDocuments()
|
|
||||||
{
|
|
||||||
if (!await _permissionService.AuthorizeAsync(StandardPermission.Security.ACCESS_ADMIN_PANEL))
|
|
||||||
return AccessDeniedView();
|
|
||||||
|
|
||||||
// Mock data for now
|
|
||||||
var model = await _dbContext.ShippingDocuments.GetAll(true).OrderByDescending(sd => sd.Created).ToListAsync();
|
|
||||||
var valami = model;
|
|
||||||
//model. = await _dbContext.GetShippingDocumentsByShippingIdAsync(shippingId);
|
|
||||||
return Json(model);
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpPost]
|
|
||||||
public async Task<IActionResult> ShippingDocumentList(int shippingId)
|
|
||||||
{
|
|
||||||
if (!await _permissionService.AuthorizeAsync(StandardPermission.Security.ACCESS_ADMIN_PANEL))
|
|
||||||
return AccessDeniedView();
|
|
||||||
|
|
||||||
// Apply filters to mock data
|
|
||||||
var model = await _dbContext.GetShippingDocumentsByShippingIdAsync(shippingId);
|
|
||||||
|
|
||||||
return Json(model);
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet]
|
|
||||||
public async Task<IActionResult> GetPartners()
|
|
||||||
{
|
|
||||||
if (!await _permissionService.AuthorizeAsync(StandardPermission.Security.ACCESS_ADMIN_PANEL))
|
|
||||||
return AccessDeniedView();
|
|
||||||
|
|
||||||
// Mock data for now
|
|
||||||
var model = await _dbContext.Partners.GetAll().OrderByDescending(p => p.Created).ToListAsync();
|
|
||||||
var valami = model;
|
|
||||||
//model. = await _dbContext.GetShippingDocumentsByShippingIdAsync(shippingId);
|
|
||||||
return Json(model);
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet]
|
|
||||||
public async Task<IActionResult> GetShippingItemsByShippingDocumentId(int shippingDocumentId)
|
|
||||||
{
|
|
||||||
if (!await _permissionService.AuthorizeAsync(StandardPermission.Security.ACCESS_ADMIN_PANEL))
|
|
||||||
return AccessDeniedView();
|
|
||||||
|
|
||||||
// Mock data for now
|
|
||||||
var model = await _dbContext.ShippingItems.GetAll(true).Where(sd => sd.ShippingDocumentId == shippingDocumentId).OrderByDescending(sd => sd.Created).ToListAsync();
|
|
||||||
var valami = model;
|
|
||||||
//model. = await _dbContext.GetShippingDocumentsByShippingIdAsync(shippingId);
|
|
||||||
return Json(model);
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet]
|
|
||||||
public async Task<IActionResult> GetShippingDocumentsByShippingDocumentId(int shippingDocumentId)
|
|
||||||
{
|
|
||||||
if (!await _permissionService.AuthorizeAsync(StandardPermission.Security.ACCESS_ADMIN_PANEL))
|
|
||||||
return AccessDeniedView();
|
|
||||||
|
|
||||||
// Mock data for now
|
|
||||||
var model = await _dbContext.ShippingDocumentToFiles.GetAll().Where(f => f.ShippingDocumentId == shippingDocumentId).OrderByDescending(sd => sd.Created).ToListAsync();
|
|
||||||
var valami = model;
|
|
||||||
//model. = await _dbContext.GetShippingDocumentsByShippingIdAsync(shippingId);
|
|
||||||
return Json(model);
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpPost]
|
|
||||||
[RequestSizeLimit(10485760)] // 10MB
|
|
||||||
[RequestFormLimits(MultipartBodyLengthLimit = 10485760)]
|
|
||||||
public async Task<IActionResult> UploadFile(UploadModel model)
|
|
||||||
{
|
|
||||||
var files = model.Files;
|
|
||||||
var shippingDocumentId = model.ShippingDocumentId;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (!await _permissionService.AuthorizeAsync(StandardPermission.Security.ACCESS_ADMIN_PANEL))
|
|
||||||
return Json(new { success = false, errorMessage = "Access denied" });
|
|
||||||
|
|
||||||
if (files == null || files.Count == 0)
|
|
||||||
return Json(new { success = false, errorMessage = "No file selected" });
|
|
||||||
|
|
||||||
foreach (var file in files)
|
|
||||||
{
|
|
||||||
// Validate file type (PDF only)
|
|
||||||
if (!file.ContentType.Equals("application/pdf", StringComparison.OrdinalIgnoreCase))
|
|
||||||
return Json(new { success = false, errorMessage = "Only PDF files are allowed" });
|
|
||||||
|
|
||||||
// Validate file size (max 10MB)
|
|
||||||
if (file.Length > 10 * 1024 * 1024)
|
|
||||||
return Json(new { 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", "shippingDocuments");
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extract text with PdfPig
|
|
||||||
var pdfText = new StringBuilder();
|
|
||||||
using (var pdf = UglyToad.PdfPig.PdfDocument.Open(filePath))
|
|
||||||
{
|
|
||||||
foreach (var page in pdf.GetPages())
|
|
||||||
{
|
|
||||||
pdfText.AppendLine(page.Text);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Log extracted text for debugging
|
|
||||||
Console.WriteLine("Extracted PDF text:");
|
|
||||||
Console.WriteLine(pdfText.ToString());
|
|
||||||
|
|
||||||
// Analyze PDF with AI to extract structured data
|
|
||||||
var aiAnalysis = await _aiCalculationService.GetOpenAIPDFAnalyzisFromText(
|
|
||||||
pdfText.ToString(),
|
|
||||||
"Extract the following information from this shipping document and return as JSON: documentDate, recipientName, senderName, invoiceNumber, totalAmount, itemCount, notes. If a field is not found, return null for that field."
|
|
||||||
);
|
|
||||||
|
|
||||||
// Parse AI response (assuming it returns JSON)
|
|
||||||
var extractedData = ParseShippingDocumentAIResponse(aiAnalysis);
|
|
||||||
|
|
||||||
//TODO: Save document record to database
|
|
||||||
Console.WriteLine("AI Analysis Result:");
|
|
||||||
Console.WriteLine(extractedData.RecipientName);
|
|
||||||
Console.WriteLine(extractedData.SenderName);
|
|
||||||
Console.WriteLine(extractedData.InvoiceNumber);
|
|
||||||
Console.WriteLine(extractedData.TotalAmount);
|
|
||||||
Console.WriteLine(extractedData.ItemCount);
|
|
||||||
Console.WriteLine(extractedData.Notes);
|
|
||||||
|
|
||||||
|
|
||||||
var documentId = 1; // Replace with: savedDocument.Id
|
|
||||||
|
|
||||||
// Return structured document model
|
|
||||||
var documentModel = new ShippingDocumentModel
|
|
||||||
{
|
|
||||||
Id = documentId,
|
|
||||||
ShippingId = shippingDocumentId,
|
|
||||||
FileName = file.FileName,
|
|
||||||
FilePath = $"/uploads/shippingDocuments/{fileName}",
|
|
||||||
FileSize = (int)(file.Length / 1024),
|
|
||||||
DocumentDate = extractedData.DocumentDate,
|
|
||||||
RecipientName = extractedData.RecipientName,
|
|
||||||
SenderName = extractedData.SenderName,
|
|
||||||
InvoiceNumber = extractedData.InvoiceNumber,
|
|
||||||
TotalAmount = extractedData.TotalAmount,
|
|
||||||
ItemCount = extractedData.ItemCount,
|
|
||||||
Notes = extractedData.Notes,
|
|
||||||
RawAIAnalysis = aiAnalysis // Store the raw AI response for debugging
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// var savedDocument = await _documentService.InsertDocumentAsync(document);
|
|
||||||
|
|
||||||
// Mock saved document ID
|
|
||||||
|
|
||||||
|
|
||||||
//return Json(new
|
|
||||||
//{
|
|
||||||
// success = true,
|
|
||||||
// document = documentModel
|
|
||||||
//});
|
|
||||||
|
|
||||||
return Ok($"Files for Shipping Document ID {shippingDocumentId} were uploaded successfully!");
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Console.WriteLine($"Error uploading file: {ex}");
|
|
||||||
//return Json(new { success = false, errorMessage = ex.Message });
|
|
||||||
return BadRequest("No files were uploaded.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private ExtractedDocumentData ParseShippingDocumentAIResponse(string aiResponse)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// Try to parse as JSON first
|
|
||||||
var data = System.Text.Json.JsonSerializer.Deserialize<ExtractedDocumentData>(
|
|
||||||
aiResponse,
|
|
||||||
new System.Text.Json.JsonSerializerOptions { PropertyNameCaseInsensitive = true }
|
|
||||||
);
|
|
||||||
return data ?? new ExtractedDocumentData();
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
// If JSON parsing fails, return empty data with the raw response in notes
|
|
||||||
return new ExtractedDocumentData
|
|
||||||
{
|
|
||||||
Notes = $"AI Analysis (raw): {aiResponse}"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helper class for extracted data
|
|
||||||
private class ExtractedDocumentData
|
|
||||||
{
|
|
||||||
public DateTime? DocumentDate { get; set; }
|
|
||||||
public string RecipientName { get; set; }
|
|
||||||
public string SenderName { get; set; }
|
|
||||||
public string InvoiceNumber { get; set; }
|
|
||||||
public decimal? TotalAmount { get; set; }
|
|
||||||
public int? ItemCount { get; set; }
|
|
||||||
public string Notes { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpPost]
|
|
||||||
public async Task<IActionResult> DeleteUploadedFile(string filePath)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (!await _permissionService.AuthorizeAsync(StandardPermission.Security.ACCESS_ADMIN_PANEL))
|
|
||||||
return Json(new { success = false, message = "Access denied" });
|
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(filePath))
|
|
||||||
return Json(new { success = false, message = "Invalid file path" });
|
|
||||||
|
|
||||||
// TODO: Delete document record from database first
|
|
||||||
// var document = await _documentService.GetDocumentByFilePathAsync(filePath);
|
|
||||||
// if (document != null)
|
|
||||||
// await _documentService.DeleteDocumentAsync(document);
|
|
||||||
|
|
||||||
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 });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public class UploadModel
|
|
||||||
{
|
|
||||||
public int ShippingDocumentId { get; set; }
|
|
||||||
public List<IFormFile> Files { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,12 +1,8 @@
|
||||||
using DevExtreme.AspNet.Data;
|
using FruitBank.Common.Entities;
|
||||||
using DevExtreme.AspNet.Mvc;
|
|
||||||
using FruitBank.Common.Entities;
|
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Models;
|
using Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Models;
|
||||||
using Nop.Plugin.Misc.FruitBankPlugin.Domains.DataLayer;
|
using Nop.Plugin.Misc.FruitBankPlugin.Domains.DataLayer;
|
||||||
using Nop.Plugin.Misc.FruitBankPlugin.Helpers;
|
|
||||||
using Nop.Plugin.Misc.FruitBankPlugin.Services;
|
|
||||||
using Nop.Services.Messages;
|
using Nop.Services.Messages;
|
||||||
using Nop.Services.Security;
|
using Nop.Services.Security;
|
||||||
using Nop.Web.Areas.Admin.Controllers;
|
using Nop.Web.Areas.Admin.Controllers;
|
||||||
|
|
@ -15,7 +11,6 @@ using Nop.Web.Framework;
|
||||||
using Nop.Web.Framework.Models;
|
using Nop.Web.Framework.Models;
|
||||||
using Nop.Web.Framework.Models.Extensions;
|
using Nop.Web.Framework.Models.Extensions;
|
||||||
using Nop.Web.Framework.Mvc.Filters;
|
using Nop.Web.Framework.Mvc.Filters;
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers
|
namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers
|
||||||
{
|
{
|
||||||
|
|
@ -29,10 +24,12 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers
|
||||||
protected readonly ShippingDbTable _shippingDbTable;
|
protected readonly ShippingDbTable _shippingDbTable;
|
||||||
protected readonly ShippingDocumentDbTable _shippingDocumentDbTable;
|
protected readonly ShippingDocumentDbTable _shippingDocumentDbTable;
|
||||||
protected readonly FruitBankDbContext _dbContext;
|
protected readonly FruitBankDbContext _dbContext;
|
||||||
protected readonly AICalculationService _aiCalculationService;
|
//private readonly IFruitBankShippingModelFactory _shippingModelFactory;
|
||||||
|
// TODO: Add your shipment and document services here
|
||||||
|
// private readonly IShipmentService _shipmentService;
|
||||||
|
// private readonly IShipmentDocumentService _documentService;
|
||||||
|
|
||||||
|
public ShippingController(IPermissionService permissionService, INotificationService notificationService, ShippingItemDbTable shippingItemDbTable, ShippingDbTable shippingDbTable, ShippingDocumentDbTable shippingDocumentDbTable, FruitBankDbContext dbContext)
|
||||||
public ShippingController(IPermissionService permissionService, INotificationService notificationService, ShippingItemDbTable shippingItemDbTable, ShippingDbTable shippingDbTable, ShippingDocumentDbTable shippingDocumentDbTable, FruitBankDbContext dbContext, AICalculationService aICalculationService)
|
|
||||||
{
|
{
|
||||||
_permissionService = permissionService;
|
_permissionService = permissionService;
|
||||||
_notificationService = notificationService;
|
_notificationService = notificationService;
|
||||||
|
|
@ -41,7 +38,6 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers
|
||||||
_shippingDocumentDbTable = shippingDocumentDbTable;
|
_shippingDocumentDbTable = shippingDocumentDbTable;
|
||||||
_dbContext = dbContext;
|
_dbContext = dbContext;
|
||||||
//_shippingModelFactory = shippingModelFactory;
|
//_shippingModelFactory = shippingModelFactory;
|
||||||
_aiCalculationService = aICalculationService;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
|
|
@ -83,8 +79,8 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers
|
||||||
return AccessDeniedView();
|
return AccessDeniedView();
|
||||||
|
|
||||||
// Create model and load data
|
// Create model and load data
|
||||||
//var model = _shippingDocumentDbTable.GetAll().ToList();
|
var model = _shippingDocumentDbTable.GetAll().ToList();
|
||||||
var model = _dbContext.ShippingDocuments.GetAll(true).ToList();
|
|
||||||
// TODO: Replace with your actual service call
|
// TODO: Replace with your actual service call
|
||||||
// model.ShippingList = await _shippingService.GetAllShippingsAsync();
|
// model.ShippingList = await _shippingService.GetAllShippingsAsync();
|
||||||
|
|
||||||
|
|
@ -106,36 +102,6 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers
|
||||||
return View("~/Plugins/Misc.FruitBankPlugin/Areas/Admin/Views/Shipping/List.cshtml", model);
|
return View("~/Plugins/Misc.FruitBankPlugin/Areas/Admin/Views/Shipping/List.cshtml", model);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//TODO IMPLEMENT DEVEXTREME FILETRING AND LOADING
|
|
||||||
//[HttpGet]
|
|
||||||
//public async Task<IActionResult> GetDocumentByShippingId(int shippingId, DataSourceLoadOptions loadOptions)
|
|
||||||
//{
|
|
||||||
// // 1. Get the IQueryable from your service.
|
|
||||||
// // This is the key step. It's more efficient to work with IQueryable
|
|
||||||
// // so that filtering, sorting, and paging are done by the database.
|
|
||||||
// var documentQuery = await _dbContext.GetShippingDocumentsByShippingIdAsync(shippingId);
|
|
||||||
|
|
||||||
// // 2. Process the data using DevExtreme.AspNet.Data.
|
|
||||||
// // This is where all the magic happens. The Load method takes care of
|
|
||||||
// // applying all the grid's client-side settings (sorting, filtering,
|
|
||||||
// // grouping, paging) to your data query.
|
|
||||||
// var loadResult = DataSourceLoader.Load(documentQuery, loadOptions);
|
|
||||||
|
|
||||||
// // 3. Return the result in the format DevExtreme expects.
|
|
||||||
// return Json(loadResult);
|
|
||||||
//}
|
|
||||||
|
|
||||||
[HttpGet]
|
|
||||||
public async Task<IActionResult> GetShippingDocumentsByShippingId(int shippingId)
|
|
||||||
{
|
|
||||||
|
|
||||||
var result = await _dbContext.GetShippingDocumentsByShippingIdAsync(shippingId);
|
|
||||||
|
|
||||||
// 3. Return the result in the format DevExtreme expects.
|
|
||||||
return Json(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
public async Task<IActionResult> Delete(int id)
|
public async Task<IActionResult> Delete(int id)
|
||||||
{
|
{
|
||||||
|
|
@ -280,28 +246,26 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
[RequestSizeLimit(10485760)] // 10MB
|
public async Task<IActionResult> UploadFile(IFormFile file, int shipmentId)
|
||||||
[RequestFormLimits(MultipartBodyLengthLimit = 10485760)]
|
|
||||||
public async Task<IActionResult> UploadFile(IFormFile file, int shippingId)
|
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (!await _permissionService.AuthorizeAsync(StandardPermission.Security.ACCESS_ADMIN_PANEL))
|
if (!await _permissionService.AuthorizeAsync(StandardPermission.Security.ACCESS_ADMIN_PANEL))
|
||||||
return Json(new { success = false, errorMessage = "Access denied" });
|
return Json(new FileUploadResult { Success = false, ErrorMessage = "Access denied" });
|
||||||
|
|
||||||
if (file == null || file.Length == 0)
|
if (file == null || file.Length == 0)
|
||||||
return Json(new { success = false, errorMessage = "No file selected" });
|
return Json(new FileUploadResult { Success = false, ErrorMessage = "No file selected" });
|
||||||
|
|
||||||
// Validate file type (PDF only)
|
// Validate file type (PDF only)
|
||||||
if (!file.ContentType.Equals("application/pdf", StringComparison.OrdinalIgnoreCase))
|
if (!file.ContentType.Equals("application/pdf", StringComparison.OrdinalIgnoreCase))
|
||||||
return Json(new { success = false, errorMessage = "Only PDF files are allowed" });
|
return Json(new FileUploadResult { Success = false, ErrorMessage = "Only PDF files are allowed" });
|
||||||
|
|
||||||
// Validate file size (max 10MB)
|
// Validate file size (e.g., max 10MB)
|
||||||
if (file.Length > 10 * 1024 * 1024)
|
if (file.Length > 10 * 1024 * 1024)
|
||||||
return Json(new { success = false, errorMessage = "File size must be less than 10MB" });
|
return Json(new FileUploadResult { Success = false, ErrorMessage = "File size must be less than 10MB" });
|
||||||
|
|
||||||
// Create upload directory if it doesn't exist
|
// Create upload directory if it doesn't exist
|
||||||
var uploadsPath = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "uploads", "shippingDocuments");
|
var uploadsPath = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "uploads", "shipments");
|
||||||
Directory.CreateDirectory(uploadsPath);
|
Directory.CreateDirectory(uploadsPath);
|
||||||
|
|
||||||
// Generate unique filename
|
// Generate unique filename
|
||||||
|
|
@ -314,108 +278,34 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers
|
||||||
await file.CopyToAsync(stream);
|
await file.CopyToAsync(stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract text with PdfPig
|
// TODO: Save document record to database
|
||||||
var pdfText = new StringBuilder();
|
// var document = new ShipmentDocument
|
||||||
using (var pdf = UglyToad.PdfPig.PdfDocument.Open(filePath))
|
// {
|
||||||
{
|
// ShipmentId = shipmentId,
|
||||||
foreach (var page in pdf.GetPages())
|
// FileName = file.FileName,
|
||||||
{
|
// FilePath = $"/uploads/shipments/{fileName}",
|
||||||
pdfText.AppendLine(page.Text);
|
// FileSize = (int)(file.Length / 1024), // Convert to KB
|
||||||
}
|
// ContentType = file.ContentType,
|
||||||
}
|
// UploadDate = DateTime.UtcNow,
|
||||||
|
// IsActive = true
|
||||||
// Log extracted text for debugging
|
// };
|
||||||
Console.WriteLine("Extracted PDF text:");
|
|
||||||
Console.WriteLine(pdfText.ToString());
|
|
||||||
|
|
||||||
// Analyze PDF with AI to extract structured data
|
|
||||||
var aiAnalysis = await _aiCalculationService.GetOpenAIPDFAnalyzisFromText(
|
|
||||||
pdfText.ToString(),
|
|
||||||
"Extract the following information from this shipping document and return as JSON: documentDate, recipientName, senderName, invoiceNumber, totalAmount, itemCount, notes. If a field is not found, return null for that field."
|
|
||||||
);
|
|
||||||
|
|
||||||
// Parse AI response (assuming it returns JSON)
|
|
||||||
var extractedData = ParseShippingDocumentAIResponse(aiAnalysis);
|
|
||||||
|
|
||||||
//TODO: Save document record to database
|
|
||||||
Console.WriteLine("AI Analysis Result:");
|
|
||||||
Console.WriteLine(extractedData.RecipientName);
|
|
||||||
Console.WriteLine(extractedData.SenderName);
|
|
||||||
Console.WriteLine(extractedData.InvoiceNumber);
|
|
||||||
Console.WriteLine(extractedData.TotalAmount);
|
|
||||||
Console.WriteLine(extractedData.ItemCount);
|
|
||||||
Console.WriteLine(extractedData.Notes);
|
|
||||||
|
|
||||||
// var savedDocument = await _documentService.InsertDocumentAsync(document);
|
// var savedDocument = await _documentService.InsertDocumentAsync(document);
|
||||||
|
|
||||||
// Mock saved document ID
|
return Json(new FileUploadResult
|
||||||
var documentId = 1; // Replace with: savedDocument.Id
|
|
||||||
|
|
||||||
// Return structured document model
|
|
||||||
var documentModel = new ShippingDocumentModel
|
|
||||||
{
|
{
|
||||||
Id = documentId,
|
Success = true,
|
||||||
ShippingId = shippingId,
|
|
||||||
FileName = file.FileName,
|
FileName = file.FileName,
|
||||||
FilePath = $"/uploads/shippingDocuments/{fileName}",
|
FilePath = $"/uploads/shippingDocuments/{fileName}",
|
||||||
FileSize = (int)(file.Length / 1024),
|
FileSize = (int)(file.Length / 1024), // KB
|
||||||
DocumentDate = extractedData.DocumentDate,
|
DocumentId = 1 // Replace with: savedDocument.Id
|
||||||
RecipientName = extractedData.RecipientName,
|
|
||||||
SenderName = extractedData.SenderName,
|
|
||||||
InvoiceNumber = extractedData.InvoiceNumber,
|
|
||||||
TotalAmount = extractedData.TotalAmount,
|
|
||||||
ItemCount = extractedData.ItemCount,
|
|
||||||
Notes = extractedData.Notes,
|
|
||||||
RawAIAnalysis = aiAnalysis // Store the raw AI response for debugging
|
|
||||||
};
|
|
||||||
|
|
||||||
return Json(new
|
|
||||||
{
|
|
||||||
success = true,
|
|
||||||
document = documentModel
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Console.WriteLine($"Error uploading file: {ex}");
|
return Json(new FileUploadResult { Success = false, ErrorMessage = ex.Message });
|
||||||
return Json(new { success = false, errorMessage = ex.Message });
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private ExtractedDocumentData ParseShippingDocumentAIResponse(string aiResponse)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
|
|
||||||
// Try to parse as JSON first
|
|
||||||
var data = System.Text.Json.JsonSerializer.Deserialize<ExtractedDocumentData>(
|
|
||||||
aiResponse,
|
|
||||||
new System.Text.Json.JsonSerializerOptions { PropertyNameCaseInsensitive = true }
|
|
||||||
);
|
|
||||||
return data ?? new ExtractedDocumentData();
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
// If JSON parsing fails, return empty data with the raw response in notes
|
|
||||||
return new ExtractedDocumentData
|
|
||||||
{
|
|
||||||
Notes = $"AI Analysis (raw): {aiResponse}"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helper class for extracted data
|
|
||||||
private class ExtractedDocumentData
|
|
||||||
{
|
|
||||||
public DateTime? DocumentDate { get; set; }
|
|
||||||
public string RecipientName { get; set; }
|
|
||||||
public string SenderName { get; set; }
|
|
||||||
public string InvoiceNumber { get; set; }
|
|
||||||
public decimal? TotalAmount { get; set; }
|
|
||||||
public int? ItemCount { get; set; }
|
|
||||||
public string Notes { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
public async Task<IActionResult> DeleteUploadedFile(string filePath)
|
public async Task<IActionResult> DeleteUploadedFile(string filePath)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -15,21 +15,12 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Models
|
||||||
[NopResourceDisplayName("Plugins.FruitBankPlugin.Fields.ModelName")]
|
[NopResourceDisplayName("Plugins.FruitBankPlugin.Fields.ModelName")]
|
||||||
public string ModelName { get; set; } = string.Empty;
|
public string ModelName { get; set; } = string.Empty;
|
||||||
|
|
||||||
[NopResourceDisplayName("Plugins.FruitBankPlugin.Fields.ApiKey")]
|
|
||||||
public string OpenAIApiKey { get; set; } = string.Empty;
|
|
||||||
|
|
||||||
[NopResourceDisplayName("Plugins.FruitBankPlugin.Fields.ModelName")]
|
|
||||||
public string OpenAIModelName { get; set; } = string.Empty;
|
|
||||||
|
|
||||||
[NopResourceDisplayName("Plugins.FruitBankPlugin.Fields.IsEnabled")]
|
[NopResourceDisplayName("Plugins.FruitBankPlugin.Fields.IsEnabled")]
|
||||||
public bool IsEnabled { get; set; }
|
public bool IsEnabled { get; set; }
|
||||||
|
|
||||||
[NopResourceDisplayName("Plugins.FruitBankPlugin.Fields.ApiBaseUrl")]
|
[NopResourceDisplayName("Plugins.FruitBankPlugin.Fields.ApiBaseUrl")]
|
||||||
public string ApiBaseUrl { get; set; } = string.Empty;
|
public string ApiBaseUrl { get; set; } = string.Empty;
|
||||||
|
|
||||||
[NopResourceDisplayName("Plugins.FruitBankPlugin.Fields.ApiBaseUrl")]
|
|
||||||
public string OpenAIApiBaseUrl { get; set; } = string.Empty;
|
|
||||||
|
|
||||||
[NopResourceDisplayName("Plugins.FruitBankPlugin.Fields.MaxTokens")]
|
[NopResourceDisplayName("Plugins.FruitBankPlugin.Fields.MaxTokens")]
|
||||||
public int MaxTokens { get; set; }
|
public int MaxTokens { get; set; }
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -27,23 +27,17 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Models
|
||||||
public class ShippingDocumentModel
|
public class ShippingDocumentModel
|
||||||
{
|
{
|
||||||
public int Id { get; set; }
|
public int Id { get; set; }
|
||||||
public int ShippingId { get; set; }
|
public ShippingDocument ShippingDocument { get; set; }
|
||||||
public string FileName { get; set; }
|
public string FileName { get; set; }
|
||||||
public string FilePath { get; set; }
|
public string FilePath { get; set; }
|
||||||
public int FileSize { get; set; }
|
public int FileSize { get; set; } // in KB
|
||||||
|
public DateTime UploadDate { get; set; }
|
||||||
// Extracted data from PDF
|
public string ContentType { get; set; }
|
||||||
public DateTime? DocumentDate { get; set; }
|
public bool IsActive { get; set; } = true;
|
||||||
public string RecipientName { get; set; }
|
|
||||||
public string SenderName { get; set; }
|
|
||||||
public string InvoiceNumber { get; set; }
|
|
||||||
public decimal? TotalAmount { get; set; }
|
|
||||||
public int? ItemCount { get; set; }
|
|
||||||
public string Notes { get; set; }
|
|
||||||
public string RawAIAnalysis { get; set; }
|
|
||||||
|
|
||||||
public ShippingDocument? ShippingDocument { get; set; }
|
|
||||||
|
|
||||||
|
// Computed properties for display
|
||||||
|
public string FormattedFileSize => $"{FileSize:N0} KB";
|
||||||
|
public string FormattedUploadDate => UploadDate.ToString("yyyy-MM-dd HH:mm");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Result model for AJAX operations
|
// Result model for AJAX operations
|
||||||
|
|
|
||||||
|
|
@ -1,13 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Models
|
|
||||||
{
|
|
||||||
public class GridBaseViewModel
|
|
||||||
{
|
|
||||||
public int? ContextId { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
using FruitBank.Common.Entities;
|
|
||||||
using Nop.Web.Framework.Models;
|
|
||||||
using Nop.Web.Framework.Mvc.ModelBinding;
|
|
||||||
|
|
||||||
namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Models
|
|
||||||
{
|
|
||||||
public record ShippingDocumentListModel : BaseNopModel
|
|
||||||
{
|
|
||||||
public ShippingDocumentListModel()
|
|
||||||
{
|
|
||||||
ShippingDocumentList = new List<ShippingDocumentModel>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public int ShippingId { get; set; }
|
|
||||||
public List<ShippingDocumentModel> ShippingDocumentList { get; set; }
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
//using Nop.Web.Framework.Models;
|
|
||||||
//using Nop.Web.Framework.Mvc.ModelBinding;
|
|
||||||
//using System.ComponentModel.DataAnnotations;
|
|
||||||
|
|
||||||
namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Models
|
|
||||||
{
|
|
||||||
public class ShippingGridPartialViewModel : GridBaseViewModel
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
//using Nop.Web.Framework.Models;
|
|
||||||
//using Nop.Web.Framework.Mvc.ModelBinding;
|
|
||||||
//using System.ComponentModel.DataAnnotations;
|
|
||||||
|
|
||||||
namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Models
|
|
||||||
{
|
|
||||||
public class TestGridModel
|
|
||||||
{
|
|
||||||
public Guid Id = Guid.NewGuid();
|
|
||||||
public string GridName { get; set; }
|
|
||||||
//public string GridControllerName { get; set; }
|
|
||||||
//public string GridEndpointName { get; set; }
|
|
||||||
public string ViewComponentName { get; set; }
|
|
||||||
public string ViewComponentLocation { get; set; }
|
|
||||||
|
|
||||||
public int? ParentRowId { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
//using Nop.Web.Framework.Models;
|
|
||||||
//using Nop.Web.Framework.Mvc.ModelBinding;
|
|
||||||
//using System.ComponentModel.DataAnnotations;
|
|
||||||
|
|
||||||
namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Models
|
|
||||||
{
|
|
||||||
public class TestPageModel
|
|
||||||
{
|
|
||||||
public List<TestGridModel> Grids { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -32,19 +32,6 @@
|
||||||
<small class="form-text text-muted">Az AI modell neve (pl. gpt-3.5-turbo, gpt-4, claude-3-sonnet)</small>
|
<small class="form-text text-muted">Az AI modell neve (pl. gpt-3.5-turbo, gpt-4, claude-3-sonnet)</small>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<label asp-for="OpenAIApiKey"></label>
|
|
||||||
<input asp-for="OpenAIApiKey" class="form-control" type="password" placeholder="Adja meg az OpenAI API kulcsot" />
|
|
||||||
<span asp-validation-for="OpenAIApiKey" class="text-danger"></span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<label asp-for="OpenAIModelName"></label>
|
|
||||||
<input asp-for="OpenAIModelName" class="form-control" placeholder="pl. gpt-3.5-turbo, gpt-4" />
|
|
||||||
<span asp-validation-for="OpenAIModelName" class="text-danger"></span>
|
|
||||||
<small class="form-text text-muted">Az AI modell neve (pl. gpt-3.5-turbo, gpt-4)</small>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label asp-for="ApiBaseUrl"></label>
|
<label asp-for="ApiBaseUrl"></label>
|
||||||
<input asp-for="ApiBaseUrl" class="form-control" placeholder="https://api.openai.com/v1" />
|
<input asp-for="ApiBaseUrl" class="form-control" placeholder="https://api.openai.com/v1" />
|
||||||
|
|
@ -52,13 +39,6 @@
|
||||||
<small class="form-text text-muted">Az API alapcíme (OpenAI, Azure OpenAI, stb.)</small>
|
<small class="form-text text-muted">Az API alapcíme (OpenAI, Azure OpenAI, stb.)</small>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<label asp-for="OpenAIApiBaseUrl"></label>
|
|
||||||
<input asp-for="OpenAIApiBaseUrl" class="form-control" placeholder="https://api.openai.com/v1" />
|
|
||||||
<span asp-validation-for="OpenAIApiBaseUrl" class="text-danger"></span>
|
|
||||||
<small class="form-text text-muted">Az API alapcíme (OpenAI, Azure OpenAI, stb.)</small>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-4">
|
<div class="col-md-4">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
|
|
|
||||||
|
|
@ -1,116 +0,0 @@
|
||||||
@model Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Models.TestGridModel
|
|
||||||
@using DevExtreme.AspNet.Mvc
|
|
||||||
|
|
||||||
@{
|
|
||||||
var contextId = Model;
|
|
||||||
// var gridId = $"dataGrid_{Guid.NewGuid():N}";
|
|
||||||
}
|
|
||||||
|
|
||||||
<div>
|
|
||||||
@(Html.DevExtreme().DataGrid()
|
|
||||||
.ID(Model.Id.ToString())
|
|
||||||
.ShowBorders(true)
|
|
||||||
.DataSource(ds => ds.Mvc()
|
|
||||||
.Controller("ManagementPage")
|
|
||||||
.LoadAction("GetPartners"))
|
|
||||||
.KeyExpr("Id")
|
|
||||||
.SearchPanel(sp => sp.Visible(true))
|
|
||||||
.HeaderFilter(hf => hf.Visible(true))
|
|
||||||
.Paging(p => p.PageSize(15))
|
|
||||||
.Pager(p => p.Visible(true))
|
|
||||||
.OnRowExpanded("onRowExpanded")
|
|
||||||
.Editing(editing => {
|
|
||||||
editing.Mode(GridEditMode.Cell);
|
|
||||||
editing.AllowUpdating(true);
|
|
||||||
editing.AllowAdding(true);
|
|
||||||
editing.AllowDeleting(true);
|
|
||||||
})
|
|
||||||
.Columns(c => {
|
|
||||||
c.Add().DataField("Id").AllowEditing(false);
|
|
||||||
c.Add().DataField("Name");
|
|
||||||
c.Add().DataField("TaxId");
|
|
||||||
c.Add().DataField("Country");
|
|
||||||
})
|
|
||||||
.Toolbar(toolbar => {
|
|
||||||
toolbar.Items(items => {
|
|
||||||
items.Add()
|
|
||||||
.Name("addRowButton")
|
|
||||||
.ShowText(ToolbarItemShowTextMode.Always);
|
|
||||||
|
|
||||||
items.Add()
|
|
||||||
.Location(ToolbarItemLocation.After)
|
|
||||||
.Widget(w =>
|
|
||||||
w.Button()
|
|
||||||
.Text("Delete Selected Records")
|
|
||||||
.Icon("trash")
|
|
||||||
.Disabled(true)
|
|
||||||
.OnClick("onDeleteBtnClick")
|
|
||||||
);
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.MasterDetail(md => {
|
|
||||||
md.Enabled(true);
|
|
||||||
md.Template(@<text>
|
|
||||||
<div class="master-detail-caption"><%- data.ShippingDate %> <%- data.LicencePlate %>'s shippingdocuments:</div>
|
|
||||||
|
|
||||||
<div id="@(new JS("'shippingDocumentGridContainer-' + data.ID"))"></div>
|
|
||||||
@(Html.DevExtreme().DataGrid<FruitBank.Common.Entities.ShippingDocument>()
|
|
||||||
.ColumnAutoWidth(true)
|
|
||||||
.ShowBorders(true)
|
|
||||||
.ID(new JS("'shippingDocumentGridContainer-' + data.Id"))
|
|
||||||
.Columns(columns => {
|
|
||||||
columns.AddFor(m => m.Id).AllowEditing(false);
|
|
||||||
|
|
||||||
columns.AddFor(m => m.Country);
|
|
||||||
|
|
||||||
columns.AddFor(m => m.Created);
|
|
||||||
|
|
||||||
columns.AddFor(m => m.PartnerId);
|
|
||||||
|
|
||||||
columns.Add()
|
|
||||||
.Caption("Completed")
|
|
||||||
.DataType(GridColumnDataType.Boolean)
|
|
||||||
.CalculateCellValue("calculateCellValue");
|
|
||||||
})
|
|
||||||
.DataSource(ds => ds.Mvc()
|
|
||||||
.Controller("Shipping")
|
|
||||||
.LoadAction("GetShippingDocumentsByShippingId")
|
|
||||||
.LoadParams(new { shippingId = new JS("data.Id") })
|
|
||||||
.Key("Id")
|
|
||||||
)
|
|
||||||
)
|
|
||||||
</text>);
|
|
||||||
})
|
|
||||||
)
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<script>
|
|
||||||
function onDeleteBtnClick(){
|
|
||||||
let dataGrid = $("#gridContainer").dxDataGrid("instance");
|
|
||||||
$.when.apply($, dataGrid.getSelectedRowsData().map(function(data) {
|
|
||||||
return dataGrid.getDataSource().store().remove(data.ID);
|
|
||||||
})).done(function() {
|
|
||||||
dataGrid.refresh();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function calculateFilterExpression(filterValue, selectedFilterOperation, target) {
|
|
||||||
if(target === "search" && typeof(filterValue) === "string") {
|
|
||||||
return [this.dataField, "contains", filterValue]
|
|
||||||
}
|
|
||||||
return function(data) {
|
|
||||||
return (data.AssignedEmployee || []).indexOf(filterValue) !== -1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function onSelectionChanged(data) {
|
|
||||||
let dataGrid = $("#gridContainer").dxDataGrid("instance");
|
|
||||||
dataGrid.option("toolbar.items[1].options.disabled", !data.selectedRowsData.length);
|
|
||||||
}
|
|
||||||
|
|
||||||
function onRowExpanded(e) {
|
|
||||||
e.component.dxDataGrid("getDataSource").reload();
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
|
|
@ -1,300 +0,0 @@
|
||||||
@model Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Models.TestGridModel
|
|
||||||
@using DevExtreme.AspNet.Mvc
|
|
||||||
|
|
||||||
@{
|
|
||||||
var contextId = Model;
|
|
||||||
// var gridId = $"dataGrid_{Guid.NewGuid():N}";
|
|
||||||
}
|
|
||||||
|
|
||||||
<div>
|
|
||||||
@(
|
|
||||||
Html.DevExtreme().DataGrid()
|
|
||||||
.ID("orderDataGridContainer")
|
|
||||||
.ShowBorders(true)
|
|
||||||
.DataSource(ds => ds.Mvc()
|
|
||||||
.Controller("ManagementPage")
|
|
||||||
.LoadAction("GetShippingDocuments"))
|
|
||||||
.KeyExpr("Id")
|
|
||||||
.SearchPanel(sp => sp.Visible(true))
|
|
||||||
.HeaderFilter(hf => hf.Visible(true))
|
|
||||||
.Paging(p => p.PageSize(15))
|
|
||||||
.Pager(p => p.Visible(true))
|
|
||||||
.OnRowExpanded("onRowExpanded")
|
|
||||||
.Editing(editing => {
|
|
||||||
editing.Mode(GridEditMode.Cell);
|
|
||||||
editing.AllowUpdating(true);
|
|
||||||
editing.AllowAdding(true);
|
|
||||||
editing.AllowDeleting(true);
|
|
||||||
})
|
|
||||||
.Columns(c => {
|
|
||||||
|
|
||||||
c.Add().DataField("Id").AllowEditing(false);
|
|
||||||
c.Add().DataField("Partner.Name").AllowEditing(false);
|
|
||||||
c.Add()
|
|
||||||
.Caption("Items in order")
|
|
||||||
.DataType(GridColumnDataType.Number)
|
|
||||||
.CalculateCellValue("calculateItemsCount").AllowEditing(false);
|
|
||||||
c.Add().DataField("PartnerId");
|
|
||||||
c.Add().DataField("DocumentIdNumber");
|
|
||||||
c.Add().DataField("IsAllMeasured");
|
|
||||||
|
|
||||||
c.Add()
|
|
||||||
.Caption("Completed")
|
|
||||||
.DataType(GridColumnDataType.Boolean)
|
|
||||||
.CalculateCellValue("calculateCellValue").AllowEditing(false);
|
|
||||||
})
|
|
||||||
.Toolbar(toolbar => {
|
|
||||||
toolbar.Items(items => {
|
|
||||||
items.Add()
|
|
||||||
.Name("addRowButton")
|
|
||||||
.ShowText(ToolbarItemShowTextMode.Always);
|
|
||||||
|
|
||||||
items.Add()
|
|
||||||
.Location(ToolbarItemLocation.After)
|
|
||||||
.Widget(w =>
|
|
||||||
w.Button()
|
|
||||||
.Text("Delete Selected Records")
|
|
||||||
.Icon("trash")
|
|
||||||
.Disabled(true)
|
|
||||||
.OnClick("onDeleteBtnClick")
|
|
||||||
);
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.MasterDetail(md => {
|
|
||||||
md.Enabled(true);
|
|
||||||
md.Template(@<text>
|
|
||||||
<div class="master-detail-caption"><%- data.ShippingDate %> <%- data.LicencePlate %>'s shippingdocuments:</div>
|
|
||||||
<div id="fileuploader">
|
|
||||||
<div class="widget-container">
|
|
||||||
|
|
||||||
<section id="tabs" class="project-tab">
|
|
||||||
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-12">
|
|
||||||
<nav>
|
|
||||||
<div class="nav nav-tabs nav-fill" id="nav-tab" role="tablist">
|
|
||||||
<a class="nav-item nav-link active" id="nav-home-tab" data-toggle="tab" href="#nav-home" role="tab" aria-controls="nav-home" aria-selected="true">Partner info</a>
|
|
||||||
<a class="nav-item nav-link" id="nav-profile-tab" data-toggle="tab" href="#nav-profile" role="tab" aria-controls="nav-profile" aria-selected="false">Products</a>
|
|
||||||
<a class="nav-item nav-link" id="nav-contact-tab" data-toggle="tab" href="#nav-contact" role="tab" aria-controls="nav-contact" aria-selected="false">Files</a>
|
|
||||||
</div>
|
|
||||||
</nav>
|
|
||||||
<div class="tab-content" id="nav-tabContent">
|
|
||||||
<div class="tab-pane fade show active" id="nav-home" role="tabpanel" aria-labelledby="nav-home-tab">
|
|
||||||
<table class="table" cellspacing="0">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Project Name</th>
|
|
||||||
<th>Employer</th>
|
|
||||||
<th>Awards</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td><a href="#">Work 1</a></td>
|
|
||||||
<td>Doe</td>
|
|
||||||
<td>john@example.com</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td><a href="#">Work 2</a></td>
|
|
||||||
<td>Moe</td>
|
|
||||||
<td>mary@example.com</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td><a href="#">Work 3</a></td>
|
|
||||||
<td>Dooley</td>
|
|
||||||
<td>july@example.com</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
<div class="tab-pane fade" id="nav-profile" role="tabpanel" aria-labelledby="nav-profile-tab">
|
|
||||||
<table class="table" cellspacing="0">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Project Name</th>
|
|
||||||
<th>Employer</th>
|
|
||||||
<th>Time</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td><a href="#">Work 1</a></td>
|
|
||||||
<td>Doe</td>
|
|
||||||
<td>john@example.com</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td><a href="#">Work 2</a></td>
|
|
||||||
<td>Moe</td>
|
|
||||||
<td>mary@example.com</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td><a href="#">Work 3</a></td>
|
|
||||||
<td>Dooley</td>
|
|
||||||
<td>july@example.com</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
<div class="tab-pane fade" id="nav-contact" role="tabpanel" aria-labelledby="nav-contact-tab">
|
|
||||||
<form method="post" enctype="multipart/form-data" asp-controller="ManagementPage" asp-action="UploadFile">
|
|
||||||
@(Html.DevExtreme().FileUploader()
|
|
||||||
.ID(new JS("'shippingDocumentUploader-' + data.Id"))
|
|
||||||
.Name("files")
|
|
||||||
.Multiple(true)
|
|
||||||
.Accept("application/pdf")
|
|
||||||
.UploadMode(FileUploadMode.UseForm)
|
|
||||||
)
|
|
||||||
|
|
||||||
<input type="hidden" name="ShippingDocumentId" value="<%- data.Id %>" />
|
|
||||||
|
|
||||||
@(Html.DevExtreme().Button()
|
|
||||||
.Text("Upload Files")
|
|
||||||
.Type(ButtonType.Success)
|
|
||||||
.UseSubmitBehavior(true)
|
|
||||||
)
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<div class="content" id="selected-files">
|
|
||||||
<div>
|
|
||||||
<h4>Selected Files</h4>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
@* <div id="@(new JS("'shippingDocumentGridContainer-' + data.ID"))"></div>
|
|
||||||
@(Html.DevExtreme().DataGrid<FruitBank.Common.Entities.ShippingDocument>()
|
|
||||||
.ColumnAutoWidth(true)
|
|
||||||
.ShowBorders(true)
|
|
||||||
.ID(new JS("'shippingDocumentGridContainer-' + data.Id"))
|
|
||||||
.Columns(columns => {
|
|
||||||
columns.AddFor(m => m.Id).AllowEditing(false);
|
|
||||||
|
|
||||||
columns.AddFor(m => m.Country);
|
|
||||||
|
|
||||||
columns.AddFor(m => m.Created);
|
|
||||||
|
|
||||||
columns.AddFor(m => m.PartnerId);
|
|
||||||
|
|
||||||
columns.Add()
|
|
||||||
.Caption("Completed")
|
|
||||||
.DataType(GridColumnDataType.Boolean)
|
|
||||||
.CalculateCellValue("calculateCellValue");
|
|
||||||
})
|
|
||||||
.DataSource(ds => ds.Mvc()
|
|
||||||
.Controller("Shipping")
|
|
||||||
.LoadAction("GetShippingDocumentsByShippingId")
|
|
||||||
.LoadParams(new { shippingId = new JS("data.Id") })
|
|
||||||
.Key("Id")
|
|
||||||
)
|
|
||||||
) *@
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</section>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</text>);
|
|
||||||
})
|
|
||||||
)
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<script>
|
|
||||||
function calculateCellValue(rowData) {
|
|
||||||
return rowData.Status === "Completed";
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
<script>
|
|
||||||
function calculateItemsCount(rowData) {
|
|
||||||
return rowData.ShippingItems.length;
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
|
|
||||||
<script>
|
|
||||||
function onDeleteBtnClick(){
|
|
||||||
let dataGrid = $("#gridContainer").dxDataGrid("instance");
|
|
||||||
$.when.apply($, dataGrid.getSelectedRowsData().map(function(data) {
|
|
||||||
return dataGrid.getDataSource().store().remove(data.ID);
|
|
||||||
})).done(function() {
|
|
||||||
dataGrid.refresh();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function calculateFilterExpression(filterValue, selectedFilterOperation, target) {
|
|
||||||
if(target === "search" && typeof(filterValue) === "string") {
|
|
||||||
return [this.dataField, "contains", filterValue]
|
|
||||||
}
|
|
||||||
return function(data) {
|
|
||||||
return (data.AssignedEmployee || []).indexOf(filterValue) !== -1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function onSelectionChanged(data) {
|
|
||||||
let dataGrid = $("#gridContainer").dxDataGrid("instance");
|
|
||||||
dataGrid.option("toolbar.items[1].options.disabled", !data.selectedRowsData.length);
|
|
||||||
}
|
|
||||||
|
|
||||||
function onRowExpanded(e) {
|
|
||||||
e.component.dxDataGrid("getDataSource").reload();
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
|
|
||||||
function fileUploader_valueChanged(e) {
|
|
||||||
var files = e.value;
|
|
||||||
if(files.length > 0) {
|
|
||||||
$("#selected-files .selected-item").remove();
|
|
||||||
|
|
||||||
$.each(files, function(i, file) {
|
|
||||||
var $selectedItem = $("<div />").addClass("selected-item");
|
|
||||||
$selectedItem.append(
|
|
||||||
$("<span />").html("Name: " + file.name + "<br/>"),
|
|
||||||
$("<span />").html("Size " + file.size + " bytes" + "<br/>"),
|
|
||||||
$("<span />").html("Type " + file.type + "<br/>"),
|
|
||||||
$("<span />").html("Last Modified Date: " + file.lastModifiedDate)
|
|
||||||
);
|
|
||||||
$selectedItem.appendTo($("#selected-files"));
|
|
||||||
});
|
|
||||||
$("#selected-files").show();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
$("#selected-files").hide();
|
|
||||||
}
|
|
||||||
|
|
||||||
function getGridInstance() {
|
|
||||||
return $("#shippingDocumentUploader").dxFileUploader("instance");
|
|
||||||
}
|
|
||||||
|
|
||||||
function fileUploader_fileUploaded(e) {
|
|
||||||
const fileUploaderId = e.component.element().attr('id');
|
|
||||||
|
|
||||||
// 2. Extract the number from the ID
|
|
||||||
const match = fileUploaderId.match(/\d+$/);
|
|
||||||
|
|
||||||
if (match) {
|
|
||||||
const uniqueId = match[0];
|
|
||||||
const gridId = `shippingDocumentGridContainer-${uniqueId}`;
|
|
||||||
|
|
||||||
// 3. Get the DevExtreme grid instance and refresh it
|
|
||||||
const grid = $(`#${gridId}`).dxDataGrid('instance');
|
|
||||||
|
|
||||||
if (grid) {
|
|
||||||
grid.dxDataGrid("getDataSource").reload();
|
|
||||||
// Optional: Show a success notification
|
|
||||||
DevExpress.ui.notify("Documents updated successfully!", "success", 2000);
|
|
||||||
} else {
|
|
||||||
console.error(`DevExtreme grid with ID "${gridId}" not found.`);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
console.error("Could not find a unique ID number from the file uploader.");
|
|
||||||
}
|
|
||||||
// shippingDocumentGridContainer
|
|
||||||
//$("#shippingDocumentGridContainer" + e.component.ID).dxDataGrid("getDataSource").reload();
|
|
||||||
}
|
|
||||||
|
|
||||||
</script>
|
|
||||||
|
|
||||||
|
|
@ -1,116 +0,0 @@
|
||||||
@model Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Models.TestGridModel
|
|
||||||
@using DevExtreme.AspNet.Mvc
|
|
||||||
|
|
||||||
@{
|
|
||||||
var contextId = Model;
|
|
||||||
// var gridId = $"dataGrid_{Guid.NewGuid():N}";
|
|
||||||
}
|
|
||||||
|
|
||||||
<div>
|
|
||||||
@(Html.DevExtreme().DataGrid()
|
|
||||||
.ID(Model.Id.ToString())
|
|
||||||
.ShowBorders(true)
|
|
||||||
.DataSource(ds => ds.Mvc()
|
|
||||||
.Controller("ManagementPage")
|
|
||||||
.LoadAction("GetShippings"))
|
|
||||||
.KeyExpr("Id")
|
|
||||||
.SearchPanel(sp => sp.Visible(true))
|
|
||||||
.HeaderFilter(hf => hf.Visible(true))
|
|
||||||
.Paging(p => p.PageSize(15))
|
|
||||||
.Pager(p => p.Visible(true))
|
|
||||||
.OnRowExpanded("onRowExpanded")
|
|
||||||
.Editing(editing => {
|
|
||||||
editing.Mode(GridEditMode.Cell);
|
|
||||||
editing.AllowUpdating(true);
|
|
||||||
editing.AllowAdding(true);
|
|
||||||
editing.AllowDeleting(true);
|
|
||||||
})
|
|
||||||
.Columns(c => {
|
|
||||||
c.Add().DataField("Id").AllowEditing(false);
|
|
||||||
c.Add().DataField("ShippingDate");
|
|
||||||
c.Add().DataField("LicencePlate");
|
|
||||||
c.Add().DataField("IsAllMeasured");
|
|
||||||
})
|
|
||||||
.Toolbar(toolbar => {
|
|
||||||
toolbar.Items(items => {
|
|
||||||
items.Add()
|
|
||||||
.Name("addRowButton")
|
|
||||||
.ShowText(ToolbarItemShowTextMode.Always);
|
|
||||||
|
|
||||||
items.Add()
|
|
||||||
.Location(ToolbarItemLocation.After)
|
|
||||||
.Widget(w =>
|
|
||||||
w.Button()
|
|
||||||
.Text("Delete Selected Records")
|
|
||||||
.Icon("trash")
|
|
||||||
.Disabled(true)
|
|
||||||
.OnClick("onDeleteBtnClick")
|
|
||||||
);
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.MasterDetail(md => {
|
|
||||||
md.Enabled(true);
|
|
||||||
md.Template(@<text>
|
|
||||||
<div class="master-detail-caption"><%- data.ShippingDate %> <%- data.LicencePlate %>'s shippingdocuments:</div>
|
|
||||||
|
|
||||||
<div id="@(new JS("'shippingDocumentGridContainer-' + data.ID"))"></div>
|
|
||||||
@(Html.DevExtreme().DataGrid<FruitBank.Common.Entities.ShippingDocument>()
|
|
||||||
.ColumnAutoWidth(true)
|
|
||||||
.ShowBorders(true)
|
|
||||||
.ID(new JS("'shippingDocumentGridContainer-' + data.Id"))
|
|
||||||
.Columns(columns => {
|
|
||||||
columns.AddFor(m => m.Id).AllowEditing(false);
|
|
||||||
|
|
||||||
columns.AddFor(m => m.Country);
|
|
||||||
|
|
||||||
columns.AddFor(m => m.Created);
|
|
||||||
|
|
||||||
columns.AddFor(m => m.PartnerId);
|
|
||||||
|
|
||||||
columns.Add()
|
|
||||||
.Caption("Completed")
|
|
||||||
.DataType(GridColumnDataType.Boolean)
|
|
||||||
.CalculateCellValue("calculateCellValue");
|
|
||||||
})
|
|
||||||
.DataSource(ds => ds.Mvc()
|
|
||||||
.Controller("Shipping")
|
|
||||||
.LoadAction("GetShippingDocumentsByShippingId")
|
|
||||||
.LoadParams(new { shippingId = new JS("data.Id") })
|
|
||||||
.Key("Id")
|
|
||||||
)
|
|
||||||
)
|
|
||||||
</text>);
|
|
||||||
})
|
|
||||||
)
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<script>
|
|
||||||
function onDeleteBtnClick(){
|
|
||||||
let dataGrid = $("#gridContainer").dxDataGrid("instance");
|
|
||||||
$.when.apply($, dataGrid.getSelectedRowsData().map(function(data) {
|
|
||||||
return dataGrid.getDataSource().store().remove(data.ID);
|
|
||||||
})).done(function() {
|
|
||||||
dataGrid.refresh();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function calculateFilterExpression(filterValue, selectedFilterOperation, target) {
|
|
||||||
if(target === "search" && typeof(filterValue) === "string") {
|
|
||||||
return [this.dataField, "contains", filterValue]
|
|
||||||
}
|
|
||||||
return function(data) {
|
|
||||||
return (data.AssignedEmployee || []).indexOf(filterValue) !== -1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function onSelectionChanged(data) {
|
|
||||||
let dataGrid = $("#gridContainer").dxDataGrid("instance");
|
|
||||||
dataGrid.option("toolbar.items[1].options.disabled", !data.selectedRowsData.length);
|
|
||||||
}
|
|
||||||
|
|
||||||
function onRowExpanded(e) {
|
|
||||||
e.component.dxDataGrid("getDataSource").reload();
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
|
|
@ -1,43 +1,4 @@
|
||||||
@model Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Models.TestPageModel
|
@{
|
||||||
|
Layout = "_AdminLayout";
|
||||||
@{
|
|
||||||
ViewData["Title"] = "Simple Tabs Example";
|
|
||||||
}
|
}
|
||||||
|
<h1>TEST VIEW FROM PLUGIN</h1>
|
||||||
<h2>@ViewData["Title"]</h2>
|
|
||||||
<section id="tabs" class="project-tab">
|
|
||||||
<div class="container-fluid">
|
|
||||||
<ul class="nav nav-tabs" id="gridTabs" role="tablist">
|
|
||||||
@for (int i = 0; i < Model.Grids.Count; i++)
|
|
||||||
{
|
|
||||||
var grid = Model.Grids[i];
|
|
||||||
<li class="nav-item">
|
|
||||||
<a class="nav-link @(i == 0 ? "active" : "")"
|
|
||||||
id="tab-@grid.Id.ToString()"
|
|
||||||
data-toggle="tab"
|
|
||||||
href="#content-@grid.Id.ToString()"
|
|
||||||
role="tab">
|
|
||||||
@grid.GridName
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
}
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<div class="tab-content mt-3" id="gridTabContent">
|
|
||||||
@for (int i = 0; i < Model.Grids.Count; i++)
|
|
||||||
{
|
|
||||||
var grid = Model.Grids[i];
|
|
||||||
<div class="tab-pane fade @(i == 0 ? "show active" : "")"
|
|
||||||
id="content-@grid.Id.ToString()"
|
|
||||||
role="tabpanel">
|
|
||||||
@await Component.InvokeAsync(grid.ViewComponentName, new { model = grid })
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -91,12 +91,32 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Existing Documents -->
|
<!-- Existing Documents -->
|
||||||
|
@if (Model.ExistingDocuments != null && Model.ExistingDocuments.Any())
|
||||||
|
{
|
||||||
<div class="mt-3">
|
<div class="mt-3">
|
||||||
<h5>Existing Documents:</h5>
|
<h5>Existing Documents:</h5>
|
||||||
<div id="documentsGridContainer">
|
<div id="existingFilesList" class="list-group">
|
||||||
@await Html.PartialAsync("~/Plugins/Misc.FruitBankPlugin/Areas/Admin/Components/_DocumentsGridPartial.cshtml", Model.ExistingDocuments)
|
@foreach (var doc in Model.ExistingDocuments)
|
||||||
|
{
|
||||||
|
<div class="file-item">
|
||||||
|
<div class="file-info">
|
||||||
|
<i class="fas fa-file-pdf"></i>
|
||||||
|
<span>@doc.FileName</span>
|
||||||
|
<small class="text-muted">(@doc.FileSize KB)</small>
|
||||||
|
</div>
|
||||||
|
<div class="file-actions">
|
||||||
|
<button type="button" class="btn btn-sm btn-outline-primary" onclick="viewFile('@doc.FilePath')">
|
||||||
|
<i class="fas fa-eye"></i> View
|
||||||
|
</button>
|
||||||
|
<button type="button" class="btn btn-sm btn-outline-danger" onclick="deleteExistingFile('@doc.Id', this)">
|
||||||
|
<i class="fas fa-trash"></i> Delete
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
<!-- Newly Uploaded Files List -->
|
<!-- Newly Uploaded Files List -->
|
||||||
<div id="uploadedFiles" class="mt-3" style="display: none;">
|
<div id="uploadedFiles" class="mt-3" style="display: none;">
|
||||||
|
|
@ -170,14 +190,6 @@
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
let grid;
|
|
||||||
|
|
||||||
function onGridInitialized(e) {
|
|
||||||
grid = e.component; // save instance
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
let uploadedFiles = [];
|
let uploadedFiles = [];
|
||||||
const ShippingId = @Model.Id;
|
const ShippingId = @Model.Id;
|
||||||
|
|
@ -187,12 +199,6 @@
|
||||||
const fileInput = document.getElementById('fileInput');
|
const fileInput = document.getElementById('fileInput');
|
||||||
const selectFilesBtn = document.getElementById('selectFilesBtn');
|
const selectFilesBtn = document.getElementById('selectFilesBtn');
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function addDocumentToGrid(doc) {
|
|
||||||
grid.getDataSource().store().insert(doc).done(() => grid.refresh());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Click to select files
|
// Click to select files
|
||||||
selectFilesBtn.addEventListener('click', () => fileInput.click());
|
selectFilesBtn.addEventListener('click', () => fileInput.click());
|
||||||
dropZone.addEventListener('click', (e) => {
|
dropZone.addEventListener('click', (e) => {
|
||||||
|
|
@ -250,17 +256,16 @@
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function uploadFile(file) {
|
function uploadFile(file) {
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
formData.append('file', file);
|
formData.append('file', file);
|
||||||
formData.append('ShippingId', ShippingId);
|
formData.append('ShippingId', ShippingId);
|
||||||
formData.append('__RequestVerificationToken', $('input[name="__RequestVerificationToken"]').val());
|
|
||||||
|
|
||||||
showUploadProgress(`Uploading and processing ${file.name}...`);
|
// Show upload progress
|
||||||
|
showUploadProgress(`Uploading ${file.name}...`);
|
||||||
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: '@Url.Action("UploadFile", "Shipping")',
|
url: '@Url.Action("UploadFile")',
|
||||||
type: 'POST',
|
type: 'POST',
|
||||||
data: formData,
|
data: formData,
|
||||||
processData: false,
|
processData: false,
|
||||||
|
|
@ -268,73 +273,43 @@
|
||||||
success: function(result) {
|
success: function(result) {
|
||||||
hideUploadProgress();
|
hideUploadProgress();
|
||||||
|
|
||||||
console.log('Upload result:', result); // Debug log
|
|
||||||
|
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
showSuccess(`"${result.document.FileName}" uploaded and processed successfully`);
|
uploadedFiles.push(result);
|
||||||
addDocumentToExistingList(result.document);
|
addFileToList(result);
|
||||||
// Call a new endpoint to get the updated partial view
|
showSuccess(`"${result.fileName}" uploaded successfully`);
|
||||||
reloadPartialView();
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
showError(`Upload failed: ${result.errorMessage}`);
|
showError(`Upload failed: ${result.errorMessage}`);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
error: function(xhr, status, error) {
|
error: function(xhr, status, error) {
|
||||||
hideUploadProgress();
|
hideUploadProgress();
|
||||||
showError(`An error occurred during the upload: ${error}`);
|
showError(`Upload failed: ${error}`);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// New function to reload the partial view
|
function addFileToList(file) {
|
||||||
function reloadPartialView() {
|
const filesList = document.getElementById('filesList');
|
||||||
// You'll need to create a new controller action for this endpoint
|
|
||||||
// that returns the partial view with the updated list of documents.
|
|
||||||
$.ajax({
|
|
||||||
url: '@Url.Action("ReloadPartialView", "Shipping")',
|
|
||||||
type: 'GET',
|
|
||||||
success: function(html) {
|
|
||||||
// Replace the content of the container holding the partial view
|
|
||||||
$('#documentsGridContainer').html(html);
|
|
||||||
},
|
|
||||||
error: function(xhr, status, error) {
|
|
||||||
console.error('Error reloading documents partial view:', error);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function addDocumentToExistingList(doc) {
|
|
||||||
const existingList = document.getElementById('existingFilesList');
|
|
||||||
|
|
||||||
const fileItem = document.createElement('div');
|
const fileItem = document.createElement('div');
|
||||||
fileItem.className = 'file-item';
|
fileItem.className = 'file-item';
|
||||||
|
|
||||||
// Use PascalCase to match C# model properties
|
|
||||||
fileItem.innerHTML = `
|
fileItem.innerHTML = `
|
||||||
<div class="file-info">
|
<div class="file-info">
|
||||||
<i class="fas fa-file-pdf"></i>
|
<i class="fas fa-file-pdf"></i>
|
||||||
<div>
|
<span>${file.fileName}</span>
|
||||||
<div><strong>${doc.FileName || 'Unnamed'}</strong> <small class="text-muted">(${doc.FileSize || 0} KB)</small></div>
|
|
||||||
${doc.DocumentDate ? `<small>Date: ${new Date(doc.DocumentDate).toLocaleDateString()}</small><br>` : ''}
|
|
||||||
${doc.RecipientName ? `<small>Recipient: ${doc.RecipientName}</small><br>` : ''}
|
|
||||||
${doc.SenderName ? `<small>Sender: ${doc.SenderName}</small><br>` : ''}
|
|
||||||
${doc.InvoiceNumber ? `<small>Invoice: ${doc.InvoiceNumber}</small><br>` : ''}
|
|
||||||
${doc.TotalAmount ? `<small>Amount: $${doc.TotalAmount}</small><br>` : ''}
|
|
||||||
${doc.ItemCount ? `<small>Items: ${doc.ItemCount}</small>` : ''}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="file-actions">
|
<div class="file-actions">
|
||||||
<button type="button" class="btn btn-sm btn-outline-primary" onclick="viewFile('${doc.FilePath}')">
|
<button type="button" class="btn btn-sm btn-outline-primary" onclick="viewFile('${file.filePath}')">
|
||||||
<i class="fas fa-eye"></i> View
|
<i class="fas fa-eye"></i> View
|
||||||
</button>
|
</button>
|
||||||
<button type="button" class="btn btn-sm btn-outline-danger" onclick="deleteExistingFile(${doc.Id}, this)">
|
<button type="button" class="btn btn-sm btn-outline-danger" onclick="deleteFile('${file.filePath}', this)">
|
||||||
<i class="fas fa-trash"></i> Delete
|
<i class="fas fa-trash"></i> Delete
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
existingList.appendChild(fileItem);
|
filesList.appendChild(fileItem);
|
||||||
|
|
||||||
|
// Show the uploaded files section
|
||||||
|
document.getElementById('uploadedFiles').style.display = 'block';
|
||||||
}
|
}
|
||||||
|
|
||||||
function showUploadProgress(message) {
|
function showUploadProgress(message) {
|
||||||
|
|
|
||||||
|
|
@ -89,69 +89,20 @@
|
||||||
.ShowBorders(true)
|
.ShowBorders(true)
|
||||||
.DataSource(list)
|
.DataSource(list)
|
||||||
.KeyExpr("Id")
|
.KeyExpr("Id")
|
||||||
.SearchPanel(sp => sp.Visible(true))
|
|
||||||
.HeaderFilter(hf => hf.Visible(true))
|
|
||||||
.Paging(p => p.PageSize(15))
|
|
||||||
.Pager(p => p.Visible(true))
|
|
||||||
.OnRowExpanded("onRowExpanded")
|
|
||||||
.Editing(editing => {
|
|
||||||
editing.Mode(GridEditMode.Cell);
|
|
||||||
editing.AllowUpdating(true);
|
|
||||||
editing.AllowAdding(true);
|
|
||||||
editing.AllowDeleting(true);
|
|
||||||
})
|
|
||||||
.Columns(c => {
|
.Columns(c => {
|
||||||
c.Add().DataField("Id").AllowEditing(false);
|
c.Add().DataField("Id");
|
||||||
c.Add().DataField("ShippingDate");
|
c.Add().DataField("ShippingDate");
|
||||||
c.Add().DataField("LicencePlate");
|
c.Add().DataField("LicencePlate");
|
||||||
c.Add().DataField("IsAllMeasured");
|
c.Add().DataField("IsAllMeasured");
|
||||||
})
|
}).MasterDetail(md => {
|
||||||
.Toolbar(toolbar => {
|
|
||||||
toolbar.Items(items => {
|
|
||||||
items.Add()
|
|
||||||
.Name("addRowButton")
|
|
||||||
.ShowText(ToolbarItemShowTextMode.Always);
|
|
||||||
|
|
||||||
items.Add()
|
|
||||||
.Location(ToolbarItemLocation.After)
|
|
||||||
.Widget(w =>
|
|
||||||
w.Button()
|
|
||||||
.Text("Delete Selected Records")
|
|
||||||
.Icon("trash")
|
|
||||||
.Disabled(true)
|
|
||||||
.OnClick("onDeleteBtnClick")
|
|
||||||
);
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.MasterDetail(md => {
|
|
||||||
md.Enabled(true);
|
md.Enabled(true);
|
||||||
md.Template(@<text>
|
md.Template(@<text>
|
||||||
<div class="master-detail-caption"><%- data.ShippingDate %> <%- data.LicencePlate %>'s shippingdocuments:</div>
|
<div class="master-detail-caption"><%- data.ShippingDate %> <%- data.LicencePlate %>'s shippingdocuments:</div>
|
||||||
div id="fileuploader">
|
|
||||||
<div class="widget-container">
|
|
||||||
@(Html.DevExtreme().FileUploader()
|
|
||||||
.ID(new JS("'shippingDocumentUploader-' + data.Id"))
|
|
||||||
.Name("myFile")
|
|
||||||
.Multiple(true)
|
|
||||||
.Accept("application/pdf")
|
|
||||||
.UploadMode(FileUploadMode.Instantly)
|
|
||||||
.UploadUrl(Url.Action("UploadFile", "Shipping"))
|
|
||||||
.OnValueChanged("fileUploader_valueChanged")
|
|
||||||
.OnUploaded("fileUploader_fileUploaded")
|
|
||||||
)
|
|
||||||
<div class="content" id="selected-files">
|
|
||||||
<div>
|
|
||||||
<h4>Selected Files</h4>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div id="@(new JS("'shippingDocumentGridContainer-' + data.ID"))"></div>
|
|
||||||
@(Html.DevExtreme().DataGrid<FruitBank.Common.Entities.ShippingDocument>()
|
@(Html.DevExtreme().DataGrid<FruitBank.Common.Entities.ShippingDocument>()
|
||||||
.ColumnAutoWidth(true)
|
.ColumnAutoWidth(true)
|
||||||
.ShowBorders(true)
|
.ShowBorders(true)
|
||||||
.ID(new JS("'shippingDocumentGridContainer-' + data.Id"))
|
|
||||||
.Columns(columns => {
|
.Columns(columns => {
|
||||||
columns.AddFor(m => m.Id).AllowEditing(false);
|
columns.AddFor(m => m.Id);
|
||||||
|
|
||||||
columns.AddFor(m => m.Country);
|
columns.AddFor(m => m.Country);
|
||||||
|
|
||||||
|
|
@ -164,18 +115,12 @@
|
||||||
.DataType(GridColumnDataType.Boolean)
|
.DataType(GridColumnDataType.Boolean)
|
||||||
.CalculateCellValue("calculateCellValue");
|
.CalculateCellValue("calculateCellValue");
|
||||||
})
|
})
|
||||||
.DataSource(ds => ds.Mvc()
|
.DataSource(new JS("data.ShippingDocuments"))
|
||||||
.Controller("Shipping")
|
|
||||||
.LoadAction("GetShippingDocumentsByShippingId")
|
|
||||||
.LoadParams(new { shippingId = new JS("data.Id") })
|
|
||||||
.Key("Id")
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
</text>);
|
</text>);
|
||||||
})
|
})
|
||||||
|
|
||||||
)
|
)
|
||||||
|
)
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
@ -185,88 +130,3 @@
|
||||||
return rowData.Status === "Completed";
|
return rowData.Status === "Completed";
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script>
|
|
||||||
function onDeleteBtnClick(){
|
|
||||||
let dataGrid = $("#gridContainer").dxDataGrid("instance");
|
|
||||||
$.when.apply($, dataGrid.getSelectedRowsData().map(function(data) {
|
|
||||||
return dataGrid.getDataSource().store().remove(data.ID);
|
|
||||||
})).done(function() {
|
|
||||||
dataGrid.refresh();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function calculateFilterExpression(filterValue, selectedFilterOperation, target) {
|
|
||||||
if(target === "search" && typeof(filterValue) === "string") {
|
|
||||||
return [this.dataField, "contains", filterValue]
|
|
||||||
}
|
|
||||||
return function(data) {
|
|
||||||
return (data.AssignedEmployee || []).indexOf(filterValue) !== -1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function onSelectionChanged(data) {
|
|
||||||
let dataGrid = $("#gridContainer").dxDataGrid("instance");
|
|
||||||
dataGrid.option("toolbar.items[1].options.disabled", !data.selectedRowsData.length);
|
|
||||||
}
|
|
||||||
|
|
||||||
function onRowExpanded(e) {
|
|
||||||
e.component.dxDataGrid("getDataSource").reload();
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
|
|
||||||
function fileUploader_valueChanged(e) {
|
|
||||||
var files = e.value;
|
|
||||||
if(files.length > 0) {
|
|
||||||
$("#selected-files .selected-item").remove();
|
|
||||||
|
|
||||||
$.each(files, function(i, file) {
|
|
||||||
var $selectedItem = $("<div />").addClass("selected-item");
|
|
||||||
$selectedItem.append(
|
|
||||||
$("<span />").html("Name: " + file.name + "<br/>"),
|
|
||||||
$("<span />").html("Size " + file.size + " bytes" + "<br/>"),
|
|
||||||
$("<span />").html("Type " + file.type + "<br/>"),
|
|
||||||
$("<span />").html("Last Modified Date: " + file.lastModifiedDate)
|
|
||||||
);
|
|
||||||
$selectedItem.appendTo($("#selected-files"));
|
|
||||||
});
|
|
||||||
$("#selected-files").show();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
$("#selected-files").hide();
|
|
||||||
}
|
|
||||||
|
|
||||||
function getGridInstance() {
|
|
||||||
return $("#shippingDocumentUploader").dxFileUploader("instance");
|
|
||||||
}
|
|
||||||
|
|
||||||
function fileUploader_fileUploaded(e) {
|
|
||||||
const fileUploaderId = e.component.element().attr('id');
|
|
||||||
|
|
||||||
// 2. Extract the number from the ID
|
|
||||||
const match = fileUploaderId.match(/\d+$/);
|
|
||||||
|
|
||||||
if (match) {
|
|
||||||
const uniqueId = match[0];
|
|
||||||
const gridId = `shippingDocumentGridContainer-${uniqueId}`;
|
|
||||||
|
|
||||||
// 3. Get the DevExtreme grid instance and refresh it
|
|
||||||
const grid = $(`#${gridId}`).dxDataGrid('instance');
|
|
||||||
|
|
||||||
if (grid) {
|
|
||||||
grid.dxDataGrid("getDataSource").reload();
|
|
||||||
// Optional: Show a success notification
|
|
||||||
DevExpress.ui.notify("Documents updated successfully!", "success", 2000);
|
|
||||||
} else {
|
|
||||||
console.error(`DevExtreme grid with ID "${gridId}" not found.`);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
console.error("Could not find a unique ID number from the file uploader.");
|
|
||||||
}
|
|
||||||
// shippingDocumentGridContainer
|
|
||||||
//$("#shippingDocumentGridContainer" + e.component.ID).dxDataGrid("getDataSource").reload();
|
|
||||||
}
|
|
||||||
|
|
||||||
</script>
|
|
||||||
|
|
|
||||||
|
|
@ -62,7 +62,7 @@ else
|
||||||
<script src="~/lib_npm/admin-lte/js/adminlte.min.js"></script>
|
<script src="~/lib_npm/admin-lte/js/adminlte.min.js"></script>
|
||||||
<script src="~/lib_npm/overlayscrollbars/browser/overlayscrollbars.browser.es6.min.js"></script>
|
<script src="~/lib_npm/overlayscrollbars/browser/overlayscrollbars.browser.es6.min.js"></script>
|
||||||
<script src="~/lib_npm/bootstrap-touchspin/jquery.bootstrap-touchspin.min.js"></script>
|
<script src="~/lib_npm/bootstrap-touchspin/jquery.bootstrap-touchspin.min.js"></script>
|
||||||
@* <script src="~/lib_npm/bootstrap/js/bootstrap.bundle.min.js"></script> *@
|
<script src="~/lib_npm/bootstrap/js/bootstrap.bundle.min.js"></script>
|
||||||
<script src="~/lib_npm/jquery-validation/jquery.validate.min.js"></script>
|
<script src="~/lib_npm/jquery-validation/jquery.validate.min.js"></script>
|
||||||
<script src="~/lib_npm/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js"></script>
|
<script src="~/lib_npm/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js"></script>
|
||||||
<script src="~/lib_npm/admin-lte/plugins/select2/js/select2.full.min.js"></script>
|
<script src="~/lib_npm/admin-lte/plugins/select2/js/select2.full.min.js"></script>
|
||||||
|
|
@ -116,7 +116,7 @@ else
|
||||||
|
|
||||||
@NopHtml.GenerateScripts(ResourceLocation.Head)
|
@NopHtml.GenerateScripts(ResourceLocation.Head)
|
||||||
@NopHtml.GenerateInlineScripts(ResourceLocation.Head)
|
@NopHtml.GenerateInlineScripts(ResourceLocation.Head)
|
||||||
<script src="~/lib_npm/bootstrap/js/bootstrap.bundle.min.js"></script>
|
|
||||||
<script src="~/Plugins/Misc.FruitBankPlugin/js/devextreme/dx.web.js"></script>
|
<script src="~/Plugins/Misc.FruitBankPlugin/js/devextreme/dx.web.js"></script>
|
||||||
<script src="~/Plugins/Misc.FruitBankPlugin/js/devextreme/aspnet/dx.aspnet.mvc.js"></script>
|
<script src="~/Plugins/Misc.FruitBankPlugin/js/devextreme/aspnet/dx.aspnet.mvc.js"></script>
|
||||||
<script src="~/Plugins/Misc.FruitBankPlugin/js/devextreme/aspnet/dx.aspnet.data.js"></script>
|
<script src="~/Plugins/Misc.FruitBankPlugin/js/devextreme/aspnet/dx.aspnet.data.js"></script>
|
||||||
|
|
@ -15,16 +15,6 @@ namespace Nop.Plugin.Misc.FruitBankPlugin
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string ModelName { get; set; } = "gpt-3.5-turbo";
|
public string ModelName { get; set; } = "gpt-3.5-turbo";
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the AI API key
|
|
||||||
/// </summary>
|
|
||||||
public string OpenAIApiKey { get; set; } = string.Empty;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the AI API model name
|
|
||||||
/// </summary>
|
|
||||||
public string OpenAIModelName { get; set; } = "gpt-3.5-turbo";
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets a value indicating whether the AI plugin is enabled
|
/// Gets or sets a value indicating whether the AI plugin is enabled
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -35,11 +25,6 @@ namespace Nop.Plugin.Misc.FruitBankPlugin
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string ApiBaseUrl { get; set; } = "https://api.openai.com/v1";
|
public string ApiBaseUrl { get; set; } = "https://api.openai.com/v1";
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the API base URL (useful for different AI providers)
|
|
||||||
/// </summary>
|
|
||||||
public string OpenAIApiBaseUrl { get; set; } = "https://api.openai.com/v1";
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the maximum number of tokens for AI responses
|
/// Gets or sets the maximum number of tokens for AI responses
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
||||||
|
|
@ -1,271 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Text.RegularExpressions;
|
|
||||||
using System.Text;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
|
|
||||||
namespace Nop.Plugin.Misc.FruitBankPlugin.Helpers;
|
|
||||||
public static class TextHelper
|
|
||||||
{
|
|
||||||
// Special character replacement map
|
|
||||||
private static readonly Dictionary<string, string> HungarianSpecialCharacterMap = new()
|
|
||||||
{
|
|
||||||
{ "/", " per " },
|
|
||||||
{ "@", " kukac " },
|
|
||||||
{ "#", " kettőskereszt " },
|
|
||||||
{ "&", " és " },
|
|
||||||
//{ ",", " vessző " },
|
|
||||||
{ " = ", " egyenlő " }, // Example, you can add more
|
|
||||||
//{ " - ", " mínusz " } // Example, you can add more
|
|
||||||
};
|
|
||||||
|
|
||||||
private static readonly Dictionary<string, string> EnglishSpecialCharacterMap = new()
|
|
||||||
{
|
|
||||||
{ "/", " slash " },
|
|
||||||
{ "@", " at " },
|
|
||||||
{ "#", " hashtag " },
|
|
||||||
{ "&", " and " },
|
|
||||||
//{ ",", " vessző " },
|
|
||||||
{ " = ", " equals " }, // Example, you can add more
|
|
||||||
//{ " - ", " mínusz " } // Example, you can add more
|
|
||||||
};
|
|
||||||
|
|
||||||
public static string ReplaceNumbersAndSpecialCharacters(string text, string language)
|
|
||||||
{
|
|
||||||
// Save parts that should be skipped (emails, URLs, dates)
|
|
||||||
var protectedParts = new Dictionary<string, string>();
|
|
||||||
|
|
||||||
// Protect dates like 2024.05.06
|
|
||||||
text = Regex.Replace(text, @"\b\d{4}\.\d{2}\.\d{2}\b", match =>
|
|
||||||
{
|
|
||||||
string key = $"__DATE__{protectedParts.Count}__";
|
|
||||||
protectedParts[key] = match.Value;
|
|
||||||
return key;
|
|
||||||
});
|
|
||||||
|
|
||||||
// Remove anything between [] including the brackets themselves
|
|
||||||
text = Regex.Replace(text, @"\[[^\]]*\]", "");
|
|
||||||
|
|
||||||
// First replace floats (keep this BEFORE integers)
|
|
||||||
text = Regex.Replace(text, @"\b\d+\.\d+\b", match =>
|
|
||||||
{
|
|
||||||
var parts = match.Value.Split('.');
|
|
||||||
var integerPart = int.Parse(parts[0]);
|
|
||||||
var decimalPart = int.Parse(parts[1]);
|
|
||||||
if(language == "Hungarian")
|
|
||||||
{
|
|
||||||
return $"{NumberToHungarian(integerPart)} egész {NumberToHungarian(decimalPart)} {(parts[1].Length == 1 ? "tized" : parts[1].Length == 2 ? "század" : "ezred")}";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return $"{NumberToEnglish(integerPart)} point {NumberToEnglish(decimalPart)}";
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Then replace integers
|
|
||||||
text = Regex.Replace(text, @"\b\d+\b", match =>
|
|
||||||
{
|
|
||||||
int number = int.Parse(match.Value);
|
|
||||||
if(language == "Hungarian")
|
|
||||||
{
|
|
||||||
return NumberToHungarian(number);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return NumberToEnglish(number);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Replace special characters from dictionary
|
|
||||||
if(language == "Hungarian")
|
|
||||||
{
|
|
||||||
foreach (var kvp in HungarianSpecialCharacterMap)
|
|
||||||
{
|
|
||||||
text = text.Replace(kvp.Key, kvp.Value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
foreach (var kvp in EnglishSpecialCharacterMap)
|
|
||||||
{
|
|
||||||
text = text.Replace(kvp.Key, kvp.Value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Replace dots surrounded by spaces (optional)
|
|
||||||
//text = Regex.Replace(text, @" (?=\.)|(?<=\.) ", " pont ");
|
|
||||||
|
|
||||||
// Restore protected parts
|
|
||||||
foreach (var kvp in protectedParts)
|
|
||||||
{
|
|
||||||
text = text.Replace(kvp.Key, kvp.Value);
|
|
||||||
}
|
|
||||||
|
|
||||||
return text;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public static string NumberToHungarian(int number)
|
|
||||||
{
|
|
||||||
if (number == 0) return "nulla";
|
|
||||||
|
|
||||||
string[] units = { "", "egy", "két", "három", "négy", "öt", "hat", "hét", "nyolc", "kilenc" };
|
|
||||||
string[] tens = { "", "tíz", "húsz", "harminc", "negyven", "ötven", "hatvan", "hetven", "nyolcvan", "kilencven" };
|
|
||||||
string[] tensAlternate = { "", "tizen", "huszon", "harminc", "negyven", "ötven", "hatvan", "hetven", "nyolcvan", "kilencven" };
|
|
||||||
|
|
||||||
StringBuilder result = new StringBuilder();
|
|
||||||
|
|
||||||
if (number >= 1000)
|
|
||||||
{
|
|
||||||
int thousands = number / 1000;
|
|
||||||
if (thousands == 1)
|
|
||||||
result.Append("ezer");
|
|
||||||
else
|
|
||||||
{
|
|
||||||
result.Append(NumberToHungarian(thousands));
|
|
||||||
result.Append("ezer");
|
|
||||||
}
|
|
||||||
number %= 1000;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (number >= 100)
|
|
||||||
{
|
|
||||||
int hundreds = number / 100;
|
|
||||||
if (hundreds == 1)
|
|
||||||
result.Append("száz");
|
|
||||||
else
|
|
||||||
{
|
|
||||||
result.Append(NumberToHungarian(hundreds));
|
|
||||||
result.Append("száz");
|
|
||||||
}
|
|
||||||
number %= 100;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (number >= 10)
|
|
||||||
{
|
|
||||||
int tensPart = number / 10;
|
|
||||||
result.Append(tensAlternate[tensPart]);
|
|
||||||
number %= 10;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (number > 0)
|
|
||||||
{
|
|
||||||
// "két" instead of "kettő" in compound numbers
|
|
||||||
if (number == 2 && result.Length > 0)
|
|
||||||
result.Append("két");
|
|
||||||
else
|
|
||||||
result.Append(units[number]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result.ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string NumberToEnglish(int number)
|
|
||||||
{
|
|
||||||
if (number == 0) return "zero";
|
|
||||||
|
|
||||||
string[] units = { "", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine" };
|
|
||||||
string[] tens = { "", "ten", "twenty", "thirty", "forty", "fifty", "sixty", "seventy", "eighty", "ninty" };
|
|
||||||
|
|
||||||
StringBuilder result = new StringBuilder();
|
|
||||||
|
|
||||||
if (number >= 1000)
|
|
||||||
{
|
|
||||||
int thousands = number / 1000;
|
|
||||||
if (thousands == 1)
|
|
||||||
result.Append("thousand");
|
|
||||||
else
|
|
||||||
{
|
|
||||||
result.Append(NumberToHungarian(thousands));
|
|
||||||
result.Append("thousand");
|
|
||||||
}
|
|
||||||
number %= 1000;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (number >= 100)
|
|
||||||
{
|
|
||||||
int hundreds = number / 100;
|
|
||||||
if (hundreds == 1)
|
|
||||||
result.Append("hundred");
|
|
||||||
else
|
|
||||||
{
|
|
||||||
result.Append(NumberToHungarian(hundreds));
|
|
||||||
result.Append("hundred");
|
|
||||||
}
|
|
||||||
number %= 100;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (number >= 10)
|
|
||||||
{
|
|
||||||
//int tensPart = number / 10;
|
|
||||||
//result.Append(tens[tensPart]);
|
|
||||||
//number %= 10;
|
|
||||||
switch (number)
|
|
||||||
{
|
|
||||||
case 10:
|
|
||||||
result.Append("ten");
|
|
||||||
break;
|
|
||||||
case 11:
|
|
||||||
result.Append("eleven");
|
|
||||||
break;
|
|
||||||
case 12:
|
|
||||||
result.Append("twelve");
|
|
||||||
break;
|
|
||||||
case 13:
|
|
||||||
result.Append("thirteen");
|
|
||||||
break;
|
|
||||||
case 14:
|
|
||||||
result.Append("fourteen");
|
|
||||||
break;
|
|
||||||
case 15:
|
|
||||||
result.Append("fifteen");
|
|
||||||
break;
|
|
||||||
case 16:
|
|
||||||
result.Append("sixteen");
|
|
||||||
break;
|
|
||||||
case 17:
|
|
||||||
result.Append("seventeen");
|
|
||||||
break;
|
|
||||||
case 18:
|
|
||||||
result.Append("eighteen");
|
|
||||||
break;
|
|
||||||
case 19:
|
|
||||||
result.Append("nineteen");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result.ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string FixJsonWithoutAI(string aiResponse)
|
|
||||||
{
|
|
||||||
if (aiResponse.StartsWith("```"))
|
|
||||||
{
|
|
||||||
|
|
||||||
//Console.WriteLine("FIXING ``` in AI Response.");
|
|
||||||
aiResponse = aiResponse.Substring(3);
|
|
||||||
if (aiResponse.StartsWith("json"))
|
|
||||||
{
|
|
||||||
aiResponse = aiResponse.Substring(4);
|
|
||||||
}
|
|
||||||
if (aiResponse.StartsWith("html"))
|
|
||||||
{
|
|
||||||
aiResponse = aiResponse.Substring(4);
|
|
||||||
}
|
|
||||||
aiResponse = aiResponse.Substring(0, aiResponse.Length - 3);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return aiResponse;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string RemoveTabs(string text)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(text)) return text;
|
|
||||||
return text.Replace("\t", ""); // Simple replace — remove all tab characters
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -79,7 +79,6 @@ public class PluginNopStartup : INopStartup
|
||||||
services.AddScoped<IOrderModelFactory, CustomOrderModelFactory>();
|
services.AddScoped<IOrderModelFactory, CustomOrderModelFactory>();
|
||||||
services.AddScoped<IGenericAttributeService, GenericAttributeService>();
|
services.AddScoped<IGenericAttributeService, GenericAttributeService>();
|
||||||
services.AddScoped<CerebrasAPIService>();
|
services.AddScoped<CerebrasAPIService>();
|
||||||
services.AddScoped<OpenAIApiService>();
|
|
||||||
//services.AddScoped<IAIAPIService, OpenAIApiService>();
|
//services.AddScoped<IAIAPIService, OpenAIApiService>();
|
||||||
services.AddScoped<AICalculationService>();
|
services.AddScoped<AICalculationService>();
|
||||||
services.AddControllersWithViews(options =>
|
services.AddControllersWithViews(options =>
|
||||||
|
|
|
||||||
|
|
@ -81,36 +81,6 @@ public class RouteProvider : IRouteProvider
|
||||||
name: "Plugin.FruitBank.Admin.Shipping.UploadFile",
|
name: "Plugin.FruitBank.Admin.Shipping.UploadFile",
|
||||||
pattern: "Admin/Shipping/UploadFile",
|
pattern: "Admin/Shipping/UploadFile",
|
||||||
defaults: new { controller = "Shipping", action = "UploadFile", area = AreaNames.ADMIN });
|
defaults: new { controller = "Shipping", action = "UploadFile", area = AreaNames.ADMIN });
|
||||||
|
|
||||||
endpointRouteBuilder.MapControllerRoute(
|
|
||||||
name: "Plugin.FruitBank.Admin.Shipping.ReloadPartialView",
|
|
||||||
pattern: "Admin/Shipping/ReloadPartialView",
|
|
||||||
defaults: new { controller = "Shipping", action = "ReloadPartialView", area = AreaNames.ADMIN });
|
|
||||||
|
|
||||||
endpointRouteBuilder.MapControllerRoute(
|
|
||||||
name: "Plugin.FruitBank.Admin.Shipping.GetShippingDocumentsByShippingId",
|
|
||||||
pattern: "Admin/Shipping/GetShippingDocumentsByShippingId",
|
|
||||||
defaults: new { controller = "Shipping", action = "GetShippingDocumentsByShippingId", area = AreaNames.ADMIN });
|
|
||||||
|
|
||||||
endpointRouteBuilder.MapControllerRoute(
|
|
||||||
name: "Plugin.FruitBank.Admin.ManagementPage.Test",
|
|
||||||
pattern: "Admin/ManagementPage/Test",
|
|
||||||
defaults: new { controller = "ManagementPage", action = "Test", area = AreaNames.ADMIN });
|
|
||||||
|
|
||||||
endpointRouteBuilder.MapControllerRoute(
|
|
||||||
name: "Plugin.FruitBank.Admin.ManagementPage.GetShippings",
|
|
||||||
pattern: "Admin/ManagementPage/GetShippings",
|
|
||||||
defaults: new { controller = "ManagementPage", action = "GetShippings", area = AreaNames.ADMIN });
|
|
||||||
|
|
||||||
endpointRouteBuilder.MapControllerRoute(
|
|
||||||
name: "Plugin.FruitBank.Admin.ManagementPage.LoadPartial",
|
|
||||||
pattern: "Admin/ManagementPage/LoadPartial",
|
|
||||||
defaults: new { controller = "ManagementPage", action = "LoadPartial", area = AreaNames.ADMIN });
|
|
||||||
|
|
||||||
endpointRouteBuilder.MapControllerRoute(
|
|
||||||
name: "Plugin.FruitBank.Admin.ManagementPage.GetPartners",
|
|
||||||
pattern: "Admin/ManagementPage/GetPartners",
|
|
||||||
defaults: new { controller = "ManagementPage", action = "GetPartners", area = AreaNames.ADMIN });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,18 @@
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Text.Json.Serialization;
|
using Newtonsoft.Json;
|
||||||
|
using Newtonsoft.Json.Serialization;
|
||||||
|
|
||||||
namespace Nop.Plugin.Misc.FruitBankPlugin.Models
|
namespace Nop.Plugin.Misc.FruitBankPlugin.Models
|
||||||
{
|
{
|
||||||
public class AIChatRequestBase
|
public class AIChatRequestBase
|
||||||
{
|
{
|
||||||
[JsonPropertyName("model")]
|
[JsonProperty("model")]
|
||||||
public string Model { get; set; } = "gpt-4o-mini";
|
public string Model { get; set; } = "gpt-4o-mini";
|
||||||
|
[JsonProperty("temperature")]
|
||||||
[JsonPropertyName("temperature")]
|
|
||||||
public double Temperature { get; set; } = 0.2;
|
public double Temperature { get; set; } = 0.2;
|
||||||
|
[JsonProperty("messages")]
|
||||||
[JsonPropertyName("messages")]
|
|
||||||
public AIChatMessage[] Messages { get; set; } = Array.Empty<AIChatMessage>();
|
public AIChatMessage[] Messages { get; set; } = Array.Empty<AIChatMessage>();
|
||||||
|
[JsonProperty("stream")]
|
||||||
[JsonPropertyName("stream")]
|
|
||||||
public bool Stream { get; set; } = true;
|
public bool Stream { get; set; } = true;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -146,18 +146,6 @@
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Update="Areas\Admin\Components\_DocumentsGridPartial.cshtml">
|
|
||||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
|
||||||
</None>
|
|
||||||
<None Update="Areas\Admin\Views\Order\ShippingDocumentGridComponent.cshtml">
|
|
||||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
|
||||||
</None>
|
|
||||||
<None Update="Areas\Admin\Views\Order\PartnersGridComponent.cshtml">
|
|
||||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
|
||||||
</None>
|
|
||||||
<None Update="Areas\Admin\Views\Order\ShippingGridComponent.cshtml">
|
|
||||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
|
||||||
</None>
|
|
||||||
<None Update="Areas\Admin\Components\_WelcomeMessage.cshtml">
|
<None Update="Areas\Admin\Components\_WelcomeMessage.cshtml">
|
||||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
</None>
|
</None>
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@
|
||||||
using Nop.Core;
|
using Nop.Core;
|
||||||
using Nop.Core.Domain.Customers;
|
using Nop.Core.Domain.Customers;
|
||||||
using Nop.Core.Domain.Stores;
|
using Nop.Core.Domain.Stores;
|
||||||
using Nop.Plugin.Misc.FruitBankPlugin.Helpers;
|
|
||||||
using StackExchange.Redis;
|
using StackExchange.Redis;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
@ -15,13 +14,11 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Services
|
||||||
public class AICalculationService
|
public class AICalculationService
|
||||||
{
|
{
|
||||||
private readonly CerebrasAPIService _cerebrasApiService;
|
private readonly CerebrasAPIService _cerebrasApiService;
|
||||||
private readonly OpenAIApiService _openAIApiService;
|
|
||||||
private readonly IStoreContext _storeContext;
|
private readonly IStoreContext _storeContext;
|
||||||
public AICalculationService(CerebrasAPIService cerebrasApiService, IStoreContext storeContext, OpenAIApiService openAIApiService)
|
public AICalculationService(CerebrasAPIService cerebrasApiService, IStoreContext storeContext)
|
||||||
{
|
{
|
||||||
_cerebrasApiService = cerebrasApiService;
|
_cerebrasApiService = cerebrasApiService;
|
||||||
_storeContext = storeContext;
|
_storeContext = storeContext;
|
||||||
_openAIApiService = openAIApiService;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<string> GetWelcomeMessageAsync(Customer customer)
|
public async Task<string> GetWelcomeMessageAsync(Customer customer)
|
||||||
|
|
@ -33,21 +30,8 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Services
|
||||||
|
|
||||||
string userMessage = "Hello";
|
string userMessage = "Hello";
|
||||||
|
|
||||||
var response = await _openAIApiService.GetSimpleResponseAsync(systemMessage, userMessage);
|
var response = await _cerebrasApiService.GetSimpleResponseAsync(systemMessage, userMessage);
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<string> GetOpenAIPDFAnalyzisFromText(string pdfText, string userQuestion)
|
|
||||||
{
|
|
||||||
string systemMessage = $"You are a helpful assistant of a webshop, you work in the administration area, with the ADMIN user. The ADMIN user is asking you questions about a PDF document, that you have access to. The content of the PDF document is the following: {pdfText}";
|
|
||||||
var response = await _openAIApiService.GetSimpleResponseAsync(systemMessage, userQuestion);
|
|
||||||
|
|
||||||
var fixedResponse = TextHelper.FixJsonWithoutAI(response);
|
|
||||||
|
|
||||||
return fixedResponse;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -30,8 +30,8 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Services
|
||||||
_httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {GetApiKey()}");
|
_httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {GetApiKey()}");
|
||||||
}
|
}
|
||||||
|
|
||||||
public string GetApiKey() => _fruitBankSettings.OpenAIApiKey;
|
public string GetApiKey() => _fruitBankSettings.ApiKey;
|
||||||
public string GetModelName() => _fruitBankSettings.OpenAIModelName;
|
public string GetModelName() => _fruitBankSettings.ModelName;
|
||||||
|
|
||||||
public void RegisterCallback(Action<string, string> callback, Action<string> onCompleteCallback, Action<string, string> onErrorCallback)
|
public void RegisterCallback(Action<string, string> callback, Action<string> onCompleteCallback, Action<string, string> onErrorCallback)
|
||||||
{
|
{
|
||||||
|
|
@ -147,7 +147,7 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Services
|
||||||
new AIChatMessage { Role = "assistant", Content = assistantMessage },
|
new AIChatMessage { Role = "assistant", Content = assistantMessage },
|
||||||
new AIChatMessage { Role = "user", Content = userMessage }
|
new AIChatMessage { Role = "user", Content = userMessage }
|
||||||
},
|
},
|
||||||
Stream = true
|
Stream = false
|
||||||
};
|
};
|
||||||
|
|
||||||
var requestJson = JsonSerializer.Serialize(requestBody, new JsonSerializerOptions
|
var requestJson = JsonSerializer.Serialize(requestBody, new JsonSerializerOptions
|
||||||
|
|
@ -175,7 +175,7 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Services
|
||||||
new AIChatMessage { Role = "assistant", Content = assistantMessage },
|
new AIChatMessage { Role = "assistant", Content = assistantMessage },
|
||||||
new AIChatMessage { Role = "user", Content = userMessage }
|
new AIChatMessage { Role = "user", Content = userMessage }
|
||||||
},
|
},
|
||||||
Stream = true
|
Stream = false
|
||||||
};
|
};
|
||||||
|
|
||||||
var requestJson = JsonSerializer.Serialize(requestBody, new JsonSerializerOptions
|
var requestJson = JsonSerializer.Serialize(requestBody, new JsonSerializerOptions
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue