266 lines
8.4 KiB
C#
266 lines
8.4 KiB
C#
|
|
using System;
|
||
|
|
using System.Collections.Generic;
|
||
|
|
using System.Linq;
|
||
|
|
using System.Text.Json;
|
||
|
|
using System.Threading.Tasks;
|
||
|
|
using Microsoft.AspNetCore.Authorization;
|
||
|
|
using Microsoft.AspNetCore.Mvc;
|
||
|
|
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||
|
|
using SkyArtShop.Models;
|
||
|
|
using SkyArtShop.Services;
|
||
|
|
|
||
|
|
namespace SkyArtShop.Controllers;
|
||
|
|
|
||
|
|
[Route("admin/products")]
|
||
|
|
[Authorize(Roles = "Admin,MasterAdmin")]
|
||
|
|
public class AdminProductsController : Controller
|
||
|
|
{
|
||
|
|
private readonly PostgreSQLService _pgService;
|
||
|
|
|
||
|
|
private readonly SlugService _slugService;
|
||
|
|
|
||
|
|
private readonly string _productsCollection = "Products";
|
||
|
|
|
||
|
|
public AdminProductsController(PostgreSQLService pgService, SlugService slugService)
|
||
|
|
{
|
||
|
|
_pgService = pgService;
|
||
|
|
_slugService = slugService;
|
||
|
|
}
|
||
|
|
|
||
|
|
[HttpGet("")]
|
||
|
|
public async Task<IActionResult> Index()
|
||
|
|
{
|
||
|
|
return View((await _pgService.GetAllAsync<Product>(_productsCollection)).OrderByDescending((Product p) => p.CreatedAt).ToList());
|
||
|
|
}
|
||
|
|
|
||
|
|
[HttpGet("create")]
|
||
|
|
public IActionResult Create()
|
||
|
|
{
|
||
|
|
return View(new Product());
|
||
|
|
}
|
||
|
|
|
||
|
|
[HttpPost("create")]
|
||
|
|
public async Task<IActionResult> Create(Product product, string? ProductVariantsJson)
|
||
|
|
{
|
||
|
|
try
|
||
|
|
{
|
||
|
|
base.ModelState.Remove("ShortDescription");
|
||
|
|
base.ModelState.Remove("Description");
|
||
|
|
base.ModelState.Remove("ProductVariantsJson");
|
||
|
|
base.ModelState.Remove("Id");
|
||
|
|
base.ModelState.Remove("Slug");
|
||
|
|
base.ModelState.Remove("ImageUrl");
|
||
|
|
base.ModelState.Remove("Images");
|
||
|
|
if (!base.ModelState.IsValid)
|
||
|
|
{
|
||
|
|
foreach (ModelError item in base.ModelState.Values.SelectMany((ModelStateEntry v) => v.Errors))
|
||
|
|
{
|
||
|
|
Console.WriteLine("[CREATE] Validation Error: " + item.ErrorMessage);
|
||
|
|
}
|
||
|
|
return View(product);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
catch (Exception ex)
|
||
|
|
{
|
||
|
|
Console.WriteLine("[CREATE] Error: " + ex.Message);
|
||
|
|
Console.WriteLine("[CREATE] Stack: " + ex.StackTrace);
|
||
|
|
base.TempData["ErrorMessage"] = "Error creating product: " + ex.Message;
|
||
|
|
return View(product);
|
||
|
|
}
|
||
|
|
if (!base.Request.Form.ContainsKey("IsActive"))
|
||
|
|
{
|
||
|
|
product.IsActive = false;
|
||
|
|
}
|
||
|
|
if (!base.Request.Form.ContainsKey("IsFeatured"))
|
||
|
|
{
|
||
|
|
product.IsFeatured = false;
|
||
|
|
}
|
||
|
|
if (!base.Request.Form.ContainsKey("IsTopSeller"))
|
||
|
|
{
|
||
|
|
product.IsTopSeller = false;
|
||
|
|
}
|
||
|
|
Console.WriteLine("[CREATE] ProductVariantsJson received: '" + (ProductVariantsJson ?? "NULL") + "'");
|
||
|
|
if (!string.IsNullOrEmpty(ProductVariantsJson))
|
||
|
|
{
|
||
|
|
try
|
||
|
|
{
|
||
|
|
product.Variants = JsonSerializer.Deserialize<List<ProductVariant>>(ProductVariantsJson) ?? new List<ProductVariant>();
|
||
|
|
Console.WriteLine($"[CREATE] Variants deserialized successfully: {product.Variants.Count} variants");
|
||
|
|
foreach (ProductVariant variant in product.Variants)
|
||
|
|
{
|
||
|
|
Console.WriteLine($" - {variant.ColorName} ({variant.ColorHex}) with {variant.Images?.Count ?? 0} images, Stock: {variant.StockQuantity}");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
catch (Exception ex2)
|
||
|
|
{
|
||
|
|
Console.WriteLine("[CREATE] Error parsing variants: " + ex2.Message);
|
||
|
|
Console.WriteLine("[CREATE] JSON was: " + ProductVariantsJson);
|
||
|
|
product.Variants = new List<ProductVariant>();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
Console.WriteLine("[CREATE] No variants provided - ProductVariantsJson is null or empty");
|
||
|
|
product.Variants = new List<ProductVariant>();
|
||
|
|
}
|
||
|
|
product.Colors = new List<string>();
|
||
|
|
product.Color = string.Empty;
|
||
|
|
try
|
||
|
|
{
|
||
|
|
product.CreatedAt = DateTime.UtcNow;
|
||
|
|
product.UpdatedAt = DateTime.UtcNow;
|
||
|
|
product.Slug = _slugService.GenerateSlug(product.Name);
|
||
|
|
await _pgService.InsertAsync(_productsCollection, product);
|
||
|
|
base.TempData["SuccessMessage"] = "Product created successfully!";
|
||
|
|
return RedirectToAction("Index");
|
||
|
|
}
|
||
|
|
catch (Exception ex3)
|
||
|
|
{
|
||
|
|
Console.WriteLine("[CREATE] Database Error: " + ex3.Message);
|
||
|
|
Console.WriteLine("[CREATE] Stack: " + ex3.StackTrace);
|
||
|
|
base.TempData["ErrorMessage"] = "Error saving product: " + ex3.Message;
|
||
|
|
return View(product);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
[HttpGet("edit/{id}")]
|
||
|
|
public async Task<IActionResult> Edit(string id)
|
||
|
|
{
|
||
|
|
Product product = await _pgService.GetByIdAsync<Product>(_productsCollection, id);
|
||
|
|
if (product == null)
|
||
|
|
{
|
||
|
|
return NotFound();
|
||
|
|
}
|
||
|
|
return View("Create", product);
|
||
|
|
}
|
||
|
|
|
||
|
|
[HttpPost("edit/{id}")]
|
||
|
|
public async Task<IActionResult> Edit(string id, Product product, string? ProductVariantsJson)
|
||
|
|
{
|
||
|
|
try
|
||
|
|
{
|
||
|
|
base.ModelState.Remove("Images");
|
||
|
|
base.ModelState.Remove("Slug");
|
||
|
|
base.ModelState.Remove("ShortDescription");
|
||
|
|
base.ModelState.Remove("Description");
|
||
|
|
base.ModelState.Remove("ProductVariantsJson");
|
||
|
|
base.ModelState.Remove("Id");
|
||
|
|
base.ModelState.Remove("ImageUrl");
|
||
|
|
if (!base.ModelState.IsValid)
|
||
|
|
{
|
||
|
|
foreach (ModelError item in base.ModelState.Values.SelectMany((ModelStateEntry v) => v.Errors))
|
||
|
|
{
|
||
|
|
Console.WriteLine("[EDIT] Validation Error: " + item.ErrorMessage);
|
||
|
|
}
|
||
|
|
return View("Create", product);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
catch (Exception ex)
|
||
|
|
{
|
||
|
|
Console.WriteLine("[EDIT] Error: " + ex.Message);
|
||
|
|
Console.WriteLine("[EDIT] Stack: " + ex.StackTrace);
|
||
|
|
base.TempData["ErrorMessage"] = "Error updating product: " + ex.Message;
|
||
|
|
return View("Create", product);
|
||
|
|
}
|
||
|
|
if (!base.Request.Form.ContainsKey("IsActive"))
|
||
|
|
{
|
||
|
|
product.IsActive = false;
|
||
|
|
}
|
||
|
|
if (!base.Request.Form.ContainsKey("IsFeatured"))
|
||
|
|
{
|
||
|
|
product.IsFeatured = false;
|
||
|
|
}
|
||
|
|
if (!base.Request.Form.ContainsKey("IsTopSeller"))
|
||
|
|
{
|
||
|
|
product.IsTopSeller = false;
|
||
|
|
}
|
||
|
|
Product existingProduct = await _pgService.GetByIdAsync<Product>(_productsCollection, id);
|
||
|
|
if (existingProduct == null)
|
||
|
|
{
|
||
|
|
base.TempData["ErrorMessage"] = "Product not found.";
|
||
|
|
return RedirectToAction("Index");
|
||
|
|
}
|
||
|
|
existingProduct.Name = product.Name;
|
||
|
|
existingProduct.ShortDescription = product.ShortDescription;
|
||
|
|
existingProduct.Description = product.Description;
|
||
|
|
existingProduct.Price = product.Price;
|
||
|
|
existingProduct.Category = product.Category;
|
||
|
|
existingProduct.Color = product.Color;
|
||
|
|
Console.WriteLine("[EDIT] ProductVariantsJson received: '" + (ProductVariantsJson ?? "NULL") + "'");
|
||
|
|
if (!string.IsNullOrEmpty(ProductVariantsJson))
|
||
|
|
{
|
||
|
|
try
|
||
|
|
{
|
||
|
|
existingProduct.Variants = JsonSerializer.Deserialize<List<ProductVariant>>(ProductVariantsJson) ?? new List<ProductVariant>();
|
||
|
|
Console.WriteLine($"[EDIT] Variants deserialized successfully: {existingProduct.Variants.Count} variants");
|
||
|
|
foreach (ProductVariant variant in existingProduct.Variants)
|
||
|
|
{
|
||
|
|
Console.WriteLine($" - {variant.ColorName} ({variant.ColorHex}) with {variant.Images?.Count ?? 0} images, Stock: {variant.StockQuantity}");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
catch (Exception ex2)
|
||
|
|
{
|
||
|
|
Console.WriteLine("[EDIT] Error parsing variants: " + ex2.Message);
|
||
|
|
Console.WriteLine("[EDIT] JSON was: " + ProductVariantsJson);
|
||
|
|
existingProduct.Variants = new List<ProductVariant>();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
Console.WriteLine("[EDIT] No variants provided - ProductVariantsJson is null or empty");
|
||
|
|
existingProduct.Variants = new List<ProductVariant>();
|
||
|
|
}
|
||
|
|
existingProduct.Colors = new List<string>();
|
||
|
|
existingProduct.Color = string.Empty;
|
||
|
|
existingProduct.StockQuantity = product.StockQuantity;
|
||
|
|
existingProduct.IsFeatured = product.IsFeatured;
|
||
|
|
existingProduct.IsTopSeller = product.IsTopSeller;
|
||
|
|
existingProduct.IsActive = product.IsActive;
|
||
|
|
existingProduct.UpdatedAt = DateTime.UtcNow;
|
||
|
|
existingProduct.Slug = _slugService.GenerateSlug(product.Name);
|
||
|
|
if (base.Request.Form.ContainsKey("Images"))
|
||
|
|
{
|
||
|
|
List<string> list = (existingProduct.Images = (from img in base.Request.Form["Images"]
|
||
|
|
where !string.IsNullOrEmpty(img)
|
||
|
|
select (img)).ToList());
|
||
|
|
if (list.Any() && string.IsNullOrEmpty(product.ImageUrl))
|
||
|
|
{
|
||
|
|
existingProduct.ImageUrl = list[0] ?? "";
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if (!string.IsNullOrEmpty(product.ImageUrl))
|
||
|
|
{
|
||
|
|
existingProduct.ImageUrl = product.ImageUrl;
|
||
|
|
}
|
||
|
|
if (!string.IsNullOrEmpty(product.SKU))
|
||
|
|
{
|
||
|
|
existingProduct.SKU = product.SKU;
|
||
|
|
}
|
||
|
|
if (product.CostPrice > 0m)
|
||
|
|
{
|
||
|
|
existingProduct.CostPrice = product.CostPrice;
|
||
|
|
}
|
||
|
|
try
|
||
|
|
{
|
||
|
|
await _pgService.UpdateAsync(_productsCollection, id, existingProduct);
|
||
|
|
base.TempData["SuccessMessage"] = "Product updated successfully!";
|
||
|
|
return RedirectToAction("Index");
|
||
|
|
}
|
||
|
|
catch (Exception ex3)
|
||
|
|
{
|
||
|
|
Console.WriteLine("[EDIT] Database Error: " + ex3.Message);
|
||
|
|
Console.WriteLine("[EDIT] Stack: " + ex3.StackTrace);
|
||
|
|
base.TempData["ErrorMessage"] = "Error saving product changes: " + ex3.Message;
|
||
|
|
return View("Create", existingProduct);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
[HttpPost("delete/{id}")]
|
||
|
|
public async Task<IActionResult> Delete(string id)
|
||
|
|
{
|
||
|
|
await _pgService.DeleteAsync<Product>(_productsCollection, id);
|
||
|
|
base.TempData["SuccessMessage"] = "Product deleted successfully!";
|
||
|
|
return RedirectToAction("Index");
|
||
|
|
}
|
||
|
|
}
|