devexpress management

This commit is contained in:
Adam 2025-10-05 14:55:46 +02:00
parent f084c3ab7f
commit 631bcdd290
6 changed files with 293 additions and 61 deletions

View File

@ -1,13 +1,16 @@
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
{
@ -17,11 +20,13 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers
{
private readonly IPermissionService _permissionService;
protected readonly FruitBankDbContext _dbContext;
protected readonly AICalculationService _aiCalculationService;
public ManagementPageController(IPermissionService permissionService, FruitBankDbContext fruitBankDbContext)
public ManagementPageController(IPermissionService permissionService, FruitBankDbContext fruitBankDbContext, AICalculationService aiCalculationService)
{
_permissionService = permissionService;
_dbContext = fruitBankDbContext;
_aiCalculationService = aiCalculationService;
}
public async Task<IActionResult> Test()
@ -101,5 +106,217 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers
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; }
}
}

View File

@ -380,14 +380,7 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers
Console.WriteLine($"Error uploading file: {ex}");
return Json(new { success = false, errorMessage = ex.Message });
}
}
//public IActionResult ReloadPartialView()
//{
// // ... (logic to get updated data for the partial view) ...
// var model = new ShippingDocumentListModel();
// return PartialView("~/Plugins/Misc.FruitBankPlugin/Areas/Admin/Components/_DocumentsGridPartial.cshtml", model);
//}
}
private ExtractedDocumentData ParseShippingDocumentAIResponse(string aiResponse)
{

View File

@ -7,36 +7,41 @@
}
<div>
@(Html.DevExtreme().DataGrid()
.ID(Model.Id.ToString())
.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("PartnerId");
@(
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.AddFor(m => m.Partner.Name); *@
c.Add()
.Caption("Completed")
.DataType(GridColumnDataType.Boolean)
.CalculateCellValue("calculateCellValue");
.CalculateCellValue("calculateCellValue").AllowEditing(false);
})
.Toolbar(toolbar => {
toolbar.Items(items => {
@ -131,21 +136,29 @@
</table>
</div>
<div class="tab-pane fade" id="nav-contact" role="tabpanel" aria-labelledby="nav-contact-tab">
@(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>
<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>()
@ -193,6 +206,12 @@
return rowData.Status === "Completed";
}
</script>
<script>
function calculateItemsCount(rowData) {
return rowData.ShippingItems.length;
}
</script>
<script>
function onDeleteBtnClick(){

View File

@ -1,20 +1,22 @@
using System.Text.Json;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using System.Text.Json.Serialization;
namespace Nop.Plugin.Misc.FruitBankPlugin.Models
{
public class AIChatRequestBase
{
[JsonProperty("model")]
[JsonPropertyName("model")]
public string Model { get; set; } = "gpt-4o-mini";
[JsonProperty("temperature")]
[JsonPropertyName("temperature")]
public double Temperature { get; set; } = 0.2;
[JsonProperty("messages")]
[JsonPropertyName("messages")]
public AIChatMessage[] Messages { get; set; } = Array.Empty<AIChatMessage>();
[JsonProperty("stream")]
[JsonPropertyName("stream")]
public bool Stream { get; set; } = true;
}
}

View File

@ -17,10 +17,11 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Services
private readonly CerebrasAPIService _cerebrasApiService;
private readonly OpenAIApiService _openAIApiService;
private readonly IStoreContext _storeContext;
public AICalculationService(CerebrasAPIService cerebrasApiService, IStoreContext storeContext)
public AICalculationService(CerebrasAPIService cerebrasApiService, IStoreContext storeContext, OpenAIApiService openAIApiService)
{
_cerebrasApiService = cerebrasApiService;
_storeContext = storeContext;
_openAIApiService = openAIApiService;
}
public async Task<string> GetWelcomeMessageAsync(Customer customer)
@ -32,14 +33,14 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Services
string userMessage = "Hello";
var response = await _cerebrasApiService.GetSimpleResponseAsync(systemMessage, userMessage);
var response = await _openAIApiService.GetSimpleResponseAsync(systemMessage, userMessage);
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 _cerebrasApiService.GetSimpleResponseAsync(systemMessage, userQuestion);
var response = await _openAIApiService.GetSimpleResponseAsync(systemMessage, userQuestion);
var fixedResponse = TextHelper.FixJsonWithoutAI(response);

View File

@ -147,7 +147,7 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Services
new AIChatMessage { Role = "assistant", Content = assistantMessage },
new AIChatMessage { Role = "user", Content = userMessage }
},
Stream = false
Stream = true
};
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 = "user", Content = userMessage }
},
Stream = false
Stream = true
};
var requestJson = JsonSerializer.Serialize(requestBody, new JsonSerializerOptions