Fix admin route access and backend configuration
- Added /admin redirect to login page in nginx config - Fixed backend server.js route ordering for proper admin handling - Updated authentication middleware and routes - Added user management routes - Configured PostgreSQL integration - Updated environment configuration
This commit is contained in:
59
Controllers/AboutController.cs
Normal file
59
Controllers/AboutController.cs
Normal file
@@ -0,0 +1,59 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using SkyArtShop.Models;
|
||||
using SkyArtShop.Services;
|
||||
|
||||
namespace SkyArtShop.Controllers;
|
||||
|
||||
public class AboutController : Controller
|
||||
{
|
||||
private readonly PostgreSQLService _pgService;
|
||||
|
||||
private readonly string _pagesCollection = "Pages";
|
||||
|
||||
public AboutController(PostgreSQLService pgService)
|
||||
{
|
||||
_pgService = pgService;
|
||||
}
|
||||
|
||||
public async Task<IActionResult> Index()
|
||||
{
|
||||
Page page = (await _pgService.GetAllAsync<Page>(_pagesCollection)).FirstOrDefault((Page p) => p.PageSlug == "about" && p.IsActive);
|
||||
Console.WriteLine($"[ABOUT] Found About page: {page != null}");
|
||||
if (page != null)
|
||||
{
|
||||
Console.WriteLine("[ABOUT] Title: " + page.Title);
|
||||
Console.WriteLine($"[ABOUT] Content length: {page.Content?.Length ?? 0}");
|
||||
Console.WriteLine($"[ABOUT] Image Gallery Count: {page.ImageGallery?.Count ?? 0}");
|
||||
Console.WriteLine($"[ABOUT] Team Members Count: {page.TeamMembers?.Count ?? 0}");
|
||||
if (page.ImageGallery != null && page.ImageGallery.Any())
|
||||
{
|
||||
Console.WriteLine("[ABOUT] Gallery Images: " + string.Join(", ", page.ImageGallery));
|
||||
}
|
||||
if (page.TeamMembers != null && page.TeamMembers.Any())
|
||||
{
|
||||
foreach (TeamMember teamMember in page.TeamMembers)
|
||||
{
|
||||
Console.WriteLine($"[ABOUT] Team: {teamMember.Name} ({teamMember.Role}) - Photo: {teamMember.PhotoUrl}");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (page == null)
|
||||
{
|
||||
page = new Page
|
||||
{
|
||||
PageName = "About",
|
||||
PageSlug = "about",
|
||||
Title = "About Sky Art Shop",
|
||||
Subtitle = "Creating moments, one craft at a time",
|
||||
Content = "<h2>Our Story</h2><p>Sky Art Shop specializes in scrapbooking, journaling, cardmaking, and collaging stationery.</p>",
|
||||
ImageGallery = new List<string>(),
|
||||
TeamMembers = new List<TeamMember>()
|
||||
};
|
||||
}
|
||||
return View(page);
|
||||
}
|
||||
}
|
||||
93
Controllers/AdminBlogController.cs
Normal file
93
Controllers/AdminBlogController.cs
Normal file
@@ -0,0 +1,93 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using SkyArtShop.Models;
|
||||
using SkyArtShop.Services;
|
||||
|
||||
namespace SkyArtShop.Controllers;
|
||||
|
||||
[Route("admin/blog")]
|
||||
[Authorize(Roles = "Admin,MasterAdmin")]
|
||||
public class AdminBlogController : Controller
|
||||
{
|
||||
private readonly PostgreSQLService _pgService;
|
||||
|
||||
private readonly SlugService _slugService;
|
||||
|
||||
private readonly string _blogCollection = "BlogPosts";
|
||||
|
||||
public AdminBlogController(PostgreSQLService pgService, SlugService slugService)
|
||||
{
|
||||
_pgService = pgService;
|
||||
_slugService = slugService;
|
||||
}
|
||||
|
||||
[HttpGet("")]
|
||||
public async Task<IActionResult> Index()
|
||||
{
|
||||
return View((await _pgService.GetAllAsync<BlogPost>(_blogCollection)).OrderByDescending((BlogPost p) => p.CreatedAt).ToList());
|
||||
}
|
||||
|
||||
[HttpGet("create")]
|
||||
public IActionResult Create()
|
||||
{
|
||||
return View(new BlogPost());
|
||||
}
|
||||
|
||||
[HttpPost("create")]
|
||||
public async Task<IActionResult> Create(BlogPost post)
|
||||
{
|
||||
if (!base.ModelState.IsValid)
|
||||
{
|
||||
return View(post);
|
||||
}
|
||||
post.CreatedAt = DateTime.UtcNow;
|
||||
post.UpdatedAt = DateTime.UtcNow;
|
||||
post.PublishedDate = DateTime.UtcNow;
|
||||
post.Slug = _slugService.GenerateSlug(post.Title);
|
||||
await _pgService.InsertAsync(_blogCollection, post);
|
||||
base.TempData["SuccessMessage"] = "Blog post created successfully!";
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
|
||||
[HttpGet("edit/{id}")]
|
||||
public async Task<IActionResult> Edit(string id)
|
||||
{
|
||||
BlogPost blogPost = await _pgService.GetByIdAsync<BlogPost>(_blogCollection, id);
|
||||
if (blogPost == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
return View(blogPost);
|
||||
}
|
||||
|
||||
[HttpPost("edit/{id}")]
|
||||
public async Task<IActionResult> Edit(string id, BlogPost post)
|
||||
{
|
||||
if (!base.ModelState.IsValid)
|
||||
{
|
||||
return View(post);
|
||||
}
|
||||
post.Id = id;
|
||||
post.UpdatedAt = DateTime.UtcNow;
|
||||
post.Slug = _slugService.GenerateSlug(post.Title);
|
||||
await _pgService.UpdateAsync(_blogCollection, id, post);
|
||||
base.TempData["SuccessMessage"] = "Blog post updated successfully!";
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
|
||||
[HttpPost("delete/{id}")]
|
||||
public async Task<IActionResult> Delete(string id)
|
||||
{
|
||||
await _pgService.DeleteAsync<BlogPost>(_blogCollection, id);
|
||||
base.TempData["SuccessMessage"] = "Blog post deleted successfully!";
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
|
||||
private string GenerateSlug(string text)
|
||||
{
|
||||
return _slugService.GenerateSlug(text);
|
||||
}
|
||||
}
|
||||
203
Controllers/AdminController.cs
Normal file
203
Controllers/AdminController.cs
Normal file
@@ -0,0 +1,203 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Security.Claims;
|
||||
using System.Security.Principal;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using SkyArtShop.Models;
|
||||
using SkyArtShop.Services;
|
||||
|
||||
namespace SkyArtShop.Controllers;
|
||||
|
||||
[Route("admin")]
|
||||
[Authorize(Roles = "Admin,MasterAdmin,Cashier,Accountant")]
|
||||
public class AdminController : Controller
|
||||
{
|
||||
private readonly PostgreSQLService _pgService;
|
||||
|
||||
private readonly PostgreAuthService _pgAuthService;
|
||||
|
||||
public AdminController(PostgreSQLService pgService, PostgreAuthService pgAuthService)
|
||||
{
|
||||
_pgService = pgService;
|
||||
_pgAuthService = pgAuthService;
|
||||
}
|
||||
|
||||
[HttpGet("login")]
|
||||
[AllowAnonymous]
|
||||
public IActionResult Login()
|
||||
{
|
||||
IIdentity? identity = base.User.Identity;
|
||||
if (identity != null && identity.IsAuthenticated)
|
||||
{
|
||||
return RedirectToAction("Dashboard");
|
||||
}
|
||||
return View();
|
||||
}
|
||||
|
||||
[HttpPost("login")]
|
||||
[AllowAnonymous]
|
||||
public async Task<IActionResult> Login(string email, string password)
|
||||
{
|
||||
AdminUser adminUser = await _pgAuthService.AuthenticateAsync(email, password);
|
||||
if (adminUser == null)
|
||||
{
|
||||
base.ViewBag.Error = "Invalid email or password";
|
||||
return View();
|
||||
}
|
||||
ClaimsPrincipal principal = _pgAuthService.CreateClaimsPrincipal(adminUser);
|
||||
await base.HttpContext.SignInAsync("Cookies", principal, new AuthenticationProperties
|
||||
{
|
||||
IsPersistent = true,
|
||||
ExpiresUtc = DateTimeOffset.UtcNow.AddDays(30.0)
|
||||
});
|
||||
return RedirectToAction("Dashboard");
|
||||
}
|
||||
|
||||
[HttpGet("logout")]
|
||||
public async Task<IActionResult> Logout()
|
||||
{
|
||||
await base.HttpContext.SignOutAsync("Cookies");
|
||||
return RedirectToAction("Login");
|
||||
}
|
||||
|
||||
[HttpGet("dashboard")]
|
||||
public async Task<IActionResult> Dashboard()
|
||||
{
|
||||
List<Product> products = await _pgService.GetAllAsync<Product>("Products");
|
||||
List<PortfolioProject> projects = await _pgService.GetAllAsync<PortfolioProject>("PortfolioProjects");
|
||||
List<BlogPost> blogPosts = await _pgService.GetAllAsync<BlogPost>("BlogPosts");
|
||||
List<Page> pages = await _pgService.GetAllAsync<Page>("Pages");
|
||||
SiteSettings siteSettings = await _pgService.GetSiteSettingsAsync();
|
||||
base.ViewBag.ProductCount = products.Count;
|
||||
base.ViewBag.ProjectCount = projects.Count;
|
||||
base.ViewBag.BlogCount = blogPosts.Count;
|
||||
base.ViewBag.PageCount = pages.Count;
|
||||
base.ViewBag.SiteName = siteSettings?.SiteName ?? "Sky Art Shop";
|
||||
base.ViewBag.AdminEmail = base.User.Identity?.Name;
|
||||
return View();
|
||||
}
|
||||
|
||||
[HttpGet("")]
|
||||
public IActionResult Index()
|
||||
{
|
||||
return RedirectToAction("Dashboard");
|
||||
}
|
||||
|
||||
[HttpGet("system-status")]
|
||||
[Authorize]
|
||||
public async Task<IActionResult> SystemStatus()
|
||||
{
|
||||
try
|
||||
{
|
||||
var value = new
|
||||
{
|
||||
databaseConnected = true,
|
||||
dbType = "PostgreSQL",
|
||||
dbHost = "localhost",
|
||||
dbName = "skyartshop",
|
||||
dbVersion = "16",
|
||||
userCount = (await _pgService.GetAllAsync<AdminUser>("AdminUsers")).Count,
|
||||
timestamp = DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss UTC")
|
||||
};
|
||||
return new JsonResult(value);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
var value2 = new
|
||||
{
|
||||
databaseConnected = false,
|
||||
error = ex.Message,
|
||||
timestamp = DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss UTC")
|
||||
};
|
||||
return new JsonResult(value2);
|
||||
}
|
||||
}
|
||||
|
||||
[HttpGet("change-password")]
|
||||
public IActionResult ChangePassword()
|
||||
{
|
||||
return View();
|
||||
}
|
||||
|
||||
[HttpGet("diagnostic")]
|
||||
public IActionResult DiagnosticTest()
|
||||
{
|
||||
return View();
|
||||
}
|
||||
|
||||
[HttpGet("reset-password-emergency")]
|
||||
[AllowAnonymous]
|
||||
public async Task<IActionResult> EmergencyPasswordReset(string confirm, string secret)
|
||||
{
|
||||
string text = Environment.GetEnvironmentVariable("EMERGENCY_RESET_SECRET") ?? "skyart-emergency-2025";
|
||||
if (secret != text)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
if (confirm != "yes-reset-now")
|
||||
{
|
||||
return Content("Add ?confirm=yes-reset-now&secret=YOUR_SECRET to URL to reset admin password");
|
||||
}
|
||||
string email = "admin@skyartshop.com";
|
||||
string newPassword = "Admin123!";
|
||||
AdminUser adminUser = await _pgService.GetUserByEmailAsync(email);
|
||||
if (adminUser == null)
|
||||
{
|
||||
adminUser = new AdminUser
|
||||
{
|
||||
Email = email,
|
||||
Name = "System Administrator",
|
||||
Role = "MasterAdmin",
|
||||
Permissions = new List<string>
|
||||
{
|
||||
"manage_users", "manage_products", "manage_orders", "manage_content", "manage_settings", "view_reports", "manage_finances", "manage_inventory", "manage_customers", "manage_blog",
|
||||
"manage_portfolio", "manage_pages"
|
||||
},
|
||||
IsActive = true,
|
||||
CreatedBy = "Emergency Reset",
|
||||
CreatedAt = DateTime.UtcNow
|
||||
};
|
||||
}
|
||||
adminUser.PasswordHash = _pgAuthService.HashPassword(newPassword);
|
||||
adminUser.LastLogin = DateTime.UtcNow;
|
||||
await _pgService.CreateAdminUserAsync(adminUser);
|
||||
return Content($"\r\n<!DOCTYPE html>\r\n<html>\r\n<head>\r\n <title>Password Reset Complete</title>\r\n <style>\r\n body {{ font-family: Arial, sans-serif; max-width: 600px; margin: 50px auto; padding: 20px; }}\r\n .success {{ background: #d4edda; border: 1px solid #c3e6cb; padding: 20px; border-radius: 5px; }}\r\n .credentials {{ background: #f8f9fa; padding: 15px; margin: 20px 0; border-left: 4px solid #28a745; }}\r\n a {{ color: #007bff; text-decoration: none; }}\r\n </style>\r\n</head>\r\n<body>\r\n <div class='success'>\r\n <h2>✓ Password Reset Successful</h2>\r\n <p>The admin password has been reset.</p>\r\n <div class='credentials'>\r\n <strong>Login Credentials:</strong><br>\r\n Email: <code>{email}</code><br>\r\n Password: <code>{newPassword}</code>\r\n </div>\r\n <p><a href='/admin/login'>→ Go to Login Page</a></p>\r\n <p><small>For security, this URL will be disabled after first successful login.</small></p>\r\n </div>\r\n</body>\r\n</html>\r\n", "text/html");
|
||||
}
|
||||
|
||||
[HttpPost("change-password")]
|
||||
public async Task<IActionResult> ChangePassword(string currentPassword, string newPassword, string confirmPassword)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(currentPassword) || string.IsNullOrWhiteSpace(newPassword))
|
||||
{
|
||||
base.ViewBag.Error = "All fields are required";
|
||||
return View();
|
||||
}
|
||||
if (newPassword != confirmPassword)
|
||||
{
|
||||
base.ViewBag.Error = "New password and confirmation do not match";
|
||||
return View();
|
||||
}
|
||||
if (newPassword.Length < 6)
|
||||
{
|
||||
base.ViewBag.Error = "Password must be at least 6 characters";
|
||||
return View();
|
||||
}
|
||||
string text = base.User.FindFirst("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier")?.Value;
|
||||
if (string.IsNullOrEmpty(text))
|
||||
{
|
||||
base.ViewBag.Error = "User not found";
|
||||
return View();
|
||||
}
|
||||
AdminUser adminUser = await _pgService.GetByIdAsync<AdminUser>("AdminUsers", text);
|
||||
if (adminUser == null || !_pgAuthService.VerifyPassword(currentPassword, adminUser.PasswordHash))
|
||||
{
|
||||
base.ViewBag.Error = "Current password is incorrect";
|
||||
return View();
|
||||
}
|
||||
base.ViewBag.Info = "Password change temporarily disabled during migration. Contact system admin.";
|
||||
return View();
|
||||
}
|
||||
}
|
||||
225
Controllers/AdminHomepageController.cs
Normal file
225
Controllers/AdminHomepageController.cs
Normal file
@@ -0,0 +1,225 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||
using SkyArtShop.Models;
|
||||
using SkyArtShop.Services;
|
||||
|
||||
namespace SkyArtShop.Controllers;
|
||||
|
||||
[Route("admin/homepage")]
|
||||
[Authorize(Roles = "Admin,MasterAdmin")]
|
||||
public class AdminHomepageController : Controller
|
||||
{
|
||||
private readonly PostgreSQLService _pgService;
|
||||
|
||||
private readonly IWebHostEnvironment _environment;
|
||||
|
||||
private readonly string _sectionsCollection = "HomepageSections";
|
||||
|
||||
private readonly string _settingsCollection = "SiteSettings";
|
||||
|
||||
public AdminHomepageController(PostgreSQLService pgService, IWebHostEnvironment environment)
|
||||
{
|
||||
_pgService = pgService;
|
||||
_environment = environment;
|
||||
}
|
||||
|
||||
[HttpGet("")]
|
||||
public async Task<IActionResult> Index()
|
||||
{
|
||||
List<HomepageSection> sections = (await _pgService.GetAllAsync<HomepageSection>(_sectionsCollection)).OrderBy((HomepageSection s) => s.DisplayOrder).ToList();
|
||||
SiteSettings siteSettings = (await _pgService.GetAllAsync<SiteSettings>(_settingsCollection)).FirstOrDefault() ?? new SiteSettings();
|
||||
base.ViewBag.Settings = siteSettings;
|
||||
return View(sections);
|
||||
}
|
||||
|
||||
[HttpGet("section/{id}")]
|
||||
public async Task<IActionResult> EditSection(string id)
|
||||
{
|
||||
HomepageSection homepageSection = await _pgService.GetByIdAsync<HomepageSection>(_sectionsCollection, id);
|
||||
if (homepageSection == null)
|
||||
{
|
||||
base.TempData["ErrorMessage"] = "Section not found.";
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
return View(homepageSection);
|
||||
}
|
||||
|
||||
[HttpPost("section/update")]
|
||||
public async Task<IActionResult> UpdateSection(HomepageSection section, IFormFile? imageFile, string? SelectedImageUrl)
|
||||
{
|
||||
base.ModelState.Remove("Content");
|
||||
base.ModelState.Remove("AdditionalData");
|
||||
base.ModelState.Remove("SelectedImageUrl");
|
||||
if (!base.ModelState.IsValid)
|
||||
{
|
||||
foreach (ModelError item in base.ModelState.Values.SelectMany((ModelStateEntry v) => v.Errors))
|
||||
{
|
||||
Console.WriteLine("ModelState Error: " + item.ErrorMessage);
|
||||
}
|
||||
return View("EditSection", section);
|
||||
}
|
||||
Console.WriteLine("Updating section with ID: " + section.Id);
|
||||
Console.WriteLine("Title: " + section.Title);
|
||||
Console.WriteLine("Subtitle: " + section.Subtitle);
|
||||
Console.WriteLine($"Content length: {section.Content?.Length ?? 0}");
|
||||
Console.WriteLine($"IsActive: {section.IsActive}");
|
||||
HomepageSection existingSection = await _pgService.GetByIdAsync<HomepageSection>(_sectionsCollection, section.Id);
|
||||
if (existingSection == null)
|
||||
{
|
||||
Console.WriteLine("ERROR: Section with ID " + section.Id + " not found!");
|
||||
base.TempData["ErrorMessage"] = "Section not found.";
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
Console.WriteLine("Found existing section: " + existingSection.Title);
|
||||
existingSection.SectionType = section.SectionType;
|
||||
existingSection.Title = section.Title ?? string.Empty;
|
||||
existingSection.Subtitle = section.Subtitle ?? string.Empty;
|
||||
existingSection.Content = section.Content ?? string.Empty;
|
||||
existingSection.ButtonText = section.ButtonText ?? string.Empty;
|
||||
existingSection.ButtonUrl = section.ButtonUrl ?? string.Empty;
|
||||
existingSection.IsActive = section.IsActive;
|
||||
existingSection.UpdatedAt = DateTime.UtcNow;
|
||||
if (existingSection.AdditionalData == null)
|
||||
{
|
||||
existingSection.AdditionalData = new Dictionary<string, string>();
|
||||
}
|
||||
if (!string.IsNullOrEmpty(SelectedImageUrl))
|
||||
{
|
||||
existingSection.ImageUrl = SelectedImageUrl;
|
||||
Console.WriteLine("Image selected from library: " + existingSection.ImageUrl);
|
||||
}
|
||||
else if (imageFile != null && imageFile.Length > 0)
|
||||
{
|
||||
string text = Path.Combine(_environment.WebRootPath, "uploads", "images");
|
||||
Directory.CreateDirectory(text);
|
||||
string uniqueFileName = $"{Guid.NewGuid()}_{imageFile.FileName}";
|
||||
string path = Path.Combine(text, uniqueFileName);
|
||||
using (FileStream fileStream = new FileStream(path, FileMode.Create))
|
||||
{
|
||||
await imageFile.CopyToAsync(fileStream);
|
||||
}
|
||||
existingSection.ImageUrl = "/uploads/images/" + uniqueFileName;
|
||||
Console.WriteLine("New image uploaded: " + existingSection.ImageUrl);
|
||||
}
|
||||
await _pgService.UpdateAsync(_sectionsCollection, existingSection.Id, existingSection);
|
||||
Console.WriteLine("Section updated successfully in database");
|
||||
base.TempData["SuccessMessage"] = "Section updated successfully!";
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
|
||||
[HttpGet("section/create")]
|
||||
public IActionResult CreateSection()
|
||||
{
|
||||
return View();
|
||||
}
|
||||
|
||||
[HttpPost("section/create")]
|
||||
public async Task<IActionResult> CreateSection(HomepageSection section, IFormFile? imageFile, string? SelectedImageUrl)
|
||||
{
|
||||
base.ModelState.Remove("Content");
|
||||
base.ModelState.Remove("AdditionalData");
|
||||
if (!base.ModelState.IsValid)
|
||||
{
|
||||
return View(section);
|
||||
}
|
||||
if (!string.IsNullOrEmpty(SelectedImageUrl))
|
||||
{
|
||||
section.ImageUrl = SelectedImageUrl;
|
||||
Console.WriteLine("Image selected from library: " + section.ImageUrl);
|
||||
}
|
||||
else if (imageFile != null && imageFile.Length > 0)
|
||||
{
|
||||
string text = Path.Combine(_environment.WebRootPath, "uploads", "images");
|
||||
Directory.CreateDirectory(text);
|
||||
string uniqueFileName = $"{Guid.NewGuid()}_{imageFile.FileName}";
|
||||
string path = Path.Combine(text, uniqueFileName);
|
||||
using (FileStream fileStream = new FileStream(path, FileMode.Create))
|
||||
{
|
||||
await imageFile.CopyToAsync(fileStream);
|
||||
}
|
||||
section.ImageUrl = "/uploads/images/" + uniqueFileName;
|
||||
}
|
||||
if (section.AdditionalData == null)
|
||||
{
|
||||
section.AdditionalData = new Dictionary<string, string>();
|
||||
}
|
||||
List<HomepageSection> source = await _pgService.GetAllAsync<HomepageSection>(_sectionsCollection);
|
||||
section.DisplayOrder = (source.Any() ? (source.Max((HomepageSection s) => s.DisplayOrder) + 1) : 0);
|
||||
section.CreatedAt = DateTime.UtcNow;
|
||||
section.UpdatedAt = DateTime.UtcNow;
|
||||
await _pgService.InsertAsync(_sectionsCollection, section);
|
||||
base.TempData["SuccessMessage"] = "Section created successfully!";
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
|
||||
[HttpPost("section/delete/{id}")]
|
||||
public async Task<IActionResult> DeleteSection(string id)
|
||||
{
|
||||
await _pgService.DeleteAsync<HomepageSection>(_sectionsCollection, id);
|
||||
base.TempData["SuccessMessage"] = "Section deleted successfully!";
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
|
||||
[HttpPost("section/reorder")]
|
||||
public async Task<IActionResult> ReorderSections([FromBody] List<string> sectionIds)
|
||||
{
|
||||
for (int i = 0; i < sectionIds.Count; i++)
|
||||
{
|
||||
HomepageSection homepageSection = await _pgService.GetByIdAsync<HomepageSection>(_sectionsCollection, sectionIds[i]);
|
||||
if (homepageSection != null)
|
||||
{
|
||||
homepageSection.DisplayOrder = i;
|
||||
homepageSection.UpdatedAt = DateTime.UtcNow;
|
||||
await _pgService.UpdateAsync(_sectionsCollection, homepageSection.Id, homepageSection);
|
||||
}
|
||||
}
|
||||
return Json(new
|
||||
{
|
||||
success = true
|
||||
});
|
||||
}
|
||||
|
||||
[HttpPost("section/toggle/{id}")]
|
||||
public async Task<IActionResult> ToggleSection(string id)
|
||||
{
|
||||
HomepageSection section = await _pgService.GetByIdAsync<HomepageSection>(_sectionsCollection, id);
|
||||
if (section != null)
|
||||
{
|
||||
section.IsActive = !section.IsActive;
|
||||
section.UpdatedAt = DateTime.UtcNow;
|
||||
await _pgService.UpdateAsync(_sectionsCollection, section.Id, section);
|
||||
base.TempData["SuccessMessage"] = "Section " + (section.IsActive ? "activated" : "deactivated") + " successfully!";
|
||||
}
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
|
||||
[HttpPost("footer/update")]
|
||||
public async Task<IActionResult> UpdateFooter(string footerText)
|
||||
{
|
||||
SiteSettings siteSettings = (await _pgService.GetAllAsync<SiteSettings>(_settingsCollection)).FirstOrDefault();
|
||||
if (siteSettings == null)
|
||||
{
|
||||
siteSettings = new SiteSettings
|
||||
{
|
||||
FooterText = footerText
|
||||
};
|
||||
await _pgService.InsertAsync(_settingsCollection, siteSettings);
|
||||
}
|
||||
else
|
||||
{
|
||||
siteSettings.FooterText = footerText;
|
||||
siteSettings.UpdatedAt = DateTime.UtcNow;
|
||||
await _pgService.UpdateAsync(_settingsCollection, siteSettings.Id, siteSettings);
|
||||
}
|
||||
base.TempData["SuccessMessage"] = "Footer updated successfully!";
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
}
|
||||
187
Controllers/AdminMenuController.cs
Normal file
187
Controllers/AdminMenuController.cs
Normal file
@@ -0,0 +1,187 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using SkyArtShop.Models;
|
||||
using SkyArtShop.Services;
|
||||
|
||||
namespace SkyArtShop.Controllers;
|
||||
|
||||
[Route("admin/menu")]
|
||||
[Authorize(Roles = "Admin,MasterAdmin")]
|
||||
public class AdminMenuController : Controller
|
||||
{
|
||||
private readonly PostgreSQLService _pgService;
|
||||
|
||||
public AdminMenuController(PostgreSQLService pgService)
|
||||
{
|
||||
_pgService = pgService;
|
||||
}
|
||||
|
||||
[HttpGet("")]
|
||||
public async Task<IActionResult> Index()
|
||||
{
|
||||
return View((await _pgService.GetAllAsync<MenuItem>("MenuItems")).OrderBy((MenuItem m) => m.DisplayOrder).ToList());
|
||||
}
|
||||
|
||||
[HttpPost("reseed")]
|
||||
public async Task<IActionResult> ReseedMenu()
|
||||
{
|
||||
foreach (MenuItem item in await _pgService.GetAllAsync<MenuItem>("MenuItems"))
|
||||
{
|
||||
await _pgService.DeleteAsync<MenuItem>("MenuItems", item.Id);
|
||||
}
|
||||
MenuItem[] array = new MenuItem[10]
|
||||
{
|
||||
new MenuItem
|
||||
{
|
||||
Label = "Home",
|
||||
Url = "/",
|
||||
DisplayOrder = 1,
|
||||
IsActive = true,
|
||||
ShowInNavbar = true,
|
||||
ShowInDropdown = true
|
||||
},
|
||||
new MenuItem
|
||||
{
|
||||
Label = "Shop",
|
||||
Url = "/Shop",
|
||||
DisplayOrder = 2,
|
||||
IsActive = true,
|
||||
ShowInNavbar = true,
|
||||
ShowInDropdown = true
|
||||
},
|
||||
new MenuItem
|
||||
{
|
||||
Label = "Top Sellers",
|
||||
Url = "/#top-sellers",
|
||||
DisplayOrder = 3,
|
||||
IsActive = true,
|
||||
ShowInNavbar = true,
|
||||
ShowInDropdown = true
|
||||
},
|
||||
new MenuItem
|
||||
{
|
||||
Label = "Promotion",
|
||||
Url = "/#promotion",
|
||||
DisplayOrder = 4,
|
||||
IsActive = true,
|
||||
ShowInNavbar = true,
|
||||
ShowInDropdown = true
|
||||
},
|
||||
new MenuItem
|
||||
{
|
||||
Label = "Portfolio",
|
||||
Url = "/Portfolio",
|
||||
DisplayOrder = 5,
|
||||
IsActive = true,
|
||||
ShowInNavbar = true,
|
||||
ShowInDropdown = true
|
||||
},
|
||||
new MenuItem
|
||||
{
|
||||
Label = "Blog",
|
||||
Url = "/Blog",
|
||||
DisplayOrder = 6,
|
||||
IsActive = true,
|
||||
ShowInNavbar = true,
|
||||
ShowInDropdown = true
|
||||
},
|
||||
new MenuItem
|
||||
{
|
||||
Label = "About",
|
||||
Url = "/About",
|
||||
DisplayOrder = 7,
|
||||
IsActive = true,
|
||||
ShowInNavbar = true,
|
||||
ShowInDropdown = true
|
||||
},
|
||||
new MenuItem
|
||||
{
|
||||
Label = "Instagram",
|
||||
Url = "#instagram",
|
||||
DisplayOrder = 8,
|
||||
IsActive = true,
|
||||
ShowInNavbar = false,
|
||||
ShowInDropdown = true
|
||||
},
|
||||
new MenuItem
|
||||
{
|
||||
Label = "Contact",
|
||||
Url = "/Contact",
|
||||
DisplayOrder = 9,
|
||||
IsActive = true,
|
||||
ShowInNavbar = true,
|
||||
ShowInDropdown = true
|
||||
},
|
||||
new MenuItem
|
||||
{
|
||||
Label = "My Wishlist",
|
||||
Url = "#wishlist",
|
||||
DisplayOrder = 10,
|
||||
IsActive = true,
|
||||
ShowInNavbar = false,
|
||||
ShowInDropdown = true
|
||||
}
|
||||
};
|
||||
MenuItem[] array2 = array;
|
||||
foreach (MenuItem entity in array2)
|
||||
{
|
||||
await _pgService.InsertAsync("MenuItems", entity);
|
||||
}
|
||||
base.TempData["SuccessMessage"] = "Menu items reseeded successfully!";
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
|
||||
[HttpGet("create")]
|
||||
public IActionResult Create()
|
||||
{
|
||||
return View(new MenuItem());
|
||||
}
|
||||
|
||||
[HttpPost("create")]
|
||||
public async Task<IActionResult> Create(MenuItem menuItem)
|
||||
{
|
||||
if (!base.ModelState.IsValid)
|
||||
{
|
||||
return View(menuItem);
|
||||
}
|
||||
menuItem.CreatedAt = DateTime.UtcNow;
|
||||
await _pgService.InsertAsync("MenuItems", menuItem);
|
||||
base.TempData["SuccessMessage"] = "Menu item created successfully!";
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
|
||||
[HttpGet("edit/{id}")]
|
||||
public async Task<IActionResult> Edit(string id)
|
||||
{
|
||||
MenuItem menuItem = await _pgService.GetByIdAsync<MenuItem>("MenuItems", id);
|
||||
if (menuItem == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
return View(menuItem);
|
||||
}
|
||||
|
||||
[HttpPost("edit/{id}")]
|
||||
public async Task<IActionResult> Edit(string id, MenuItem menuItem)
|
||||
{
|
||||
if (!base.ModelState.IsValid)
|
||||
{
|
||||
return View(menuItem);
|
||||
}
|
||||
menuItem.Id = id;
|
||||
await _pgService.UpdateAsync("MenuItems", id, menuItem);
|
||||
base.TempData["SuccessMessage"] = "Menu item updated successfully!";
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
|
||||
[HttpPost("delete/{id}")]
|
||||
public async Task<IActionResult> Delete(string id)
|
||||
{
|
||||
await _pgService.DeleteAsync<MenuItem>("MenuItems", id);
|
||||
base.TempData["SuccessMessage"] = "Menu item deleted successfully!";
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
}
|
||||
163
Controllers/AdminPagesController.cs
Normal file
163
Controllers/AdminPagesController.cs
Normal file
@@ -0,0 +1,163 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||
using SkyArtShop.Models;
|
||||
using SkyArtShop.Services;
|
||||
|
||||
namespace SkyArtShop.Controllers;
|
||||
|
||||
[Route("admin/pages")]
|
||||
[Authorize(Roles = "Admin,MasterAdmin")]
|
||||
public class AdminPagesController : Controller
|
||||
{
|
||||
private readonly PostgreSQLService _pgService;
|
||||
|
||||
private readonly SlugService _slugService;
|
||||
|
||||
private readonly string _pagesCollection = "Pages";
|
||||
|
||||
public AdminPagesController(PostgreSQLService pgService, SlugService slugService)
|
||||
{
|
||||
_pgService = pgService;
|
||||
_slugService = slugService;
|
||||
}
|
||||
|
||||
[HttpGet("")]
|
||||
public async Task<IActionResult> Index()
|
||||
{
|
||||
return View((await _pgService.GetAllAsync<Page>(_pagesCollection)).OrderBy((Page p) => p.PageName).ToList());
|
||||
}
|
||||
|
||||
[HttpGet("create")]
|
||||
public IActionResult Create()
|
||||
{
|
||||
return View(new Page());
|
||||
}
|
||||
|
||||
[HttpPost("create")]
|
||||
public async Task<IActionResult> Create(Page page)
|
||||
{
|
||||
if (!base.ModelState.IsValid)
|
||||
{
|
||||
return View(page);
|
||||
}
|
||||
page.CreatedAt = DateTime.UtcNow;
|
||||
page.UpdatedAt = DateTime.UtcNow;
|
||||
page.PageSlug = _slugService.GenerateSlug(page.PageName);
|
||||
await _pgService.InsertAsync(_pagesCollection, page);
|
||||
base.TempData["SuccessMessage"] = "Page created successfully!";
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
|
||||
[HttpGet("edit/{id}")]
|
||||
public async Task<IActionResult> Edit(string id)
|
||||
{
|
||||
Page page = await _pgService.GetByIdAsync<Page>(_pagesCollection, id);
|
||||
if (page == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
return View(page);
|
||||
}
|
||||
|
||||
[HttpPost("edit/{id}")]
|
||||
public async Task<IActionResult> Edit(string id, [FromForm] Page page, IFormCollection form)
|
||||
{
|
||||
Console.WriteLine("[ADMIN-PAGES] === FORM SUBMISSION DEBUG ===");
|
||||
Console.WriteLine("[ADMIN-PAGES] Form Keys: " + string.Join(", ", form.Keys));
|
||||
foreach (string key in form.Keys)
|
||||
{
|
||||
if (key.StartsWith("ImageGallery") || key.StartsWith("TeamMembers"))
|
||||
{
|
||||
Console.WriteLine($"[ADMIN-PAGES] {key} = {form[key]}");
|
||||
}
|
||||
}
|
||||
if (!base.ModelState.IsValid)
|
||||
{
|
||||
Console.WriteLine("[ADMIN-PAGES] ModelState is INVALID");
|
||||
foreach (ModelError item2 in base.ModelState.Values.SelectMany((ModelStateEntry v) => v.Errors))
|
||||
{
|
||||
Console.WriteLine("[ADMIN-PAGES] Error: " + item2.ErrorMessage);
|
||||
}
|
||||
return View(page);
|
||||
}
|
||||
Page page2 = await _pgService.GetByIdAsync<Page>(_pagesCollection, id);
|
||||
if (page2 == null)
|
||||
{
|
||||
Console.WriteLine("[ADMIN-PAGES] Page not found: " + id);
|
||||
return NotFound();
|
||||
}
|
||||
page2.PageName = page.PageName;
|
||||
page2.Title = page.Title;
|
||||
page2.Subtitle = page.Subtitle;
|
||||
page2.Content = page.Content;
|
||||
page2.IsActive = page.IsActive;
|
||||
page2.UpdatedAt = DateTime.UtcNow;
|
||||
page2.PageSlug = _slugService.GenerateSlug(page.PageName);
|
||||
page2.AboutImage1 = form["AboutImage1"].ToString() ?? string.Empty;
|
||||
page2.AboutImage2 = form["AboutImage2"].ToString() ?? string.Empty;
|
||||
page2.ImageGallery = new List<string>();
|
||||
foreach (string item3 in form.Keys.Where((string k) => k.StartsWith("ImageGallery[")))
|
||||
{
|
||||
string text = form[item3].ToString();
|
||||
if (!string.IsNullOrEmpty(text))
|
||||
{
|
||||
page2.ImageGallery.Add(text);
|
||||
}
|
||||
}
|
||||
page2.TeamMembers = new List<TeamMember>();
|
||||
List<int> list = (from i in (from i in form.Keys.Where((string k) => k.StartsWith("TeamMembers[") && k.Contains("].Name")).Select(delegate(string k)
|
||||
{
|
||||
Match match = Regex.Match(k, "TeamMembers\\[(\\d+)\\]");
|
||||
return (!match.Success) ? (-1) : int.Parse(match.Groups[1].Value);
|
||||
})
|
||||
where i >= 0
|
||||
select i).Distinct()
|
||||
orderby i
|
||||
select i).ToList();
|
||||
foreach (int item4 in list)
|
||||
{
|
||||
TeamMember item = new TeamMember
|
||||
{
|
||||
Name = form[$"TeamMembers[{item4}].Name"].ToString(),
|
||||
Role = form[$"TeamMembers[{item4}].Role"].ToString(),
|
||||
Bio = form[$"TeamMembers[{item4}].Bio"].ToString(),
|
||||
PhotoUrl = form[$"TeamMembers[{item4}].PhotoUrl"].ToString()
|
||||
};
|
||||
page2.TeamMembers.Add(item);
|
||||
}
|
||||
Console.WriteLine($"[ADMIN-PAGES] Updating page: {page2.PageName} (Slug: {page2.PageSlug})");
|
||||
Console.WriteLine("[ADMIN-PAGES] Title: " + page2.Title);
|
||||
Console.WriteLine($"[ADMIN-PAGES] Content length: {page2.Content?.Length ?? 0}");
|
||||
Console.WriteLine($"[ADMIN-PAGES] Image Gallery Count: {page2.ImageGallery.Count}");
|
||||
Console.WriteLine($"[ADMIN-PAGES] Team Members Count: {page2.TeamMembers.Count}");
|
||||
if (page2.ImageGallery.Any())
|
||||
{
|
||||
Console.WriteLine("[ADMIN-PAGES] Gallery Images: " + string.Join(", ", page2.ImageGallery));
|
||||
}
|
||||
if (page2.TeamMembers.Any())
|
||||
{
|
||||
foreach (TeamMember teamMember in page2.TeamMembers)
|
||||
{
|
||||
Console.WriteLine($"[ADMIN-PAGES] Team Member: {teamMember.Name} - {teamMember.Role} - Photo: {teamMember.PhotoUrl}");
|
||||
}
|
||||
}
|
||||
await _pgService.UpdateAsync(_pagesCollection, id, page2);
|
||||
base.TempData["SuccessMessage"] = "Page updated successfully!";
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
|
||||
[HttpPost("delete/{id}")]
|
||||
public async Task<IActionResult> Delete(string id)
|
||||
{
|
||||
await _pgService.DeleteAsync<Page>(_pagesCollection, id);
|
||||
base.TempData["SuccessMessage"] = "Page deleted successfully!";
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
}
|
||||
165
Controllers/AdminPortfolioController.cs
Normal file
165
Controllers/AdminPortfolioController.cs
Normal file
@@ -0,0 +1,165 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using SkyArtShop.Models;
|
||||
using SkyArtShop.Services;
|
||||
|
||||
namespace SkyArtShop.Controllers;
|
||||
|
||||
[Route("admin/portfolio")]
|
||||
[Authorize(Roles = "Admin,MasterAdmin")]
|
||||
public class AdminPortfolioController : Controller
|
||||
{
|
||||
private readonly PostgreSQLService _pgService;
|
||||
|
||||
private readonly SlugService _slugService;
|
||||
|
||||
private readonly string _categoriesCollection = "PortfolioCategories";
|
||||
|
||||
private readonly string _projectsCollection = "PortfolioProjects";
|
||||
|
||||
public AdminPortfolioController(PostgreSQLService pgService, SlugService slugService)
|
||||
{
|
||||
_pgService = pgService;
|
||||
_slugService = slugService;
|
||||
}
|
||||
|
||||
[HttpGet("categories")]
|
||||
public async Task<IActionResult> Categories()
|
||||
{
|
||||
return View((await _pgService.GetAllAsync<PortfolioCategory>(_categoriesCollection)).OrderBy((PortfolioCategory c) => c.DisplayOrder).ToList());
|
||||
}
|
||||
|
||||
[HttpGet("category/create")]
|
||||
public IActionResult CreateCategory()
|
||||
{
|
||||
return View(new PortfolioCategory());
|
||||
}
|
||||
|
||||
[HttpPost("category/create")]
|
||||
public async Task<IActionResult> CreateCategory(PortfolioCategory category)
|
||||
{
|
||||
if (!base.ModelState.IsValid)
|
||||
{
|
||||
return View(category);
|
||||
}
|
||||
category.CreatedAt = DateTime.UtcNow;
|
||||
category.UpdatedAt = DateTime.UtcNow;
|
||||
category.Slug = _slugService.GenerateSlug(category.Name);
|
||||
await _pgService.InsertAsync(_categoriesCollection, category);
|
||||
base.TempData["SuccessMessage"] = "Category created successfully!";
|
||||
return RedirectToAction("Categories");
|
||||
}
|
||||
|
||||
[HttpGet("category/edit/{id}")]
|
||||
public async Task<IActionResult> EditCategory(string id)
|
||||
{
|
||||
PortfolioCategory portfolioCategory = await _pgService.GetByIdAsync<PortfolioCategory>(_categoriesCollection, id);
|
||||
if (portfolioCategory == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
return View(portfolioCategory);
|
||||
}
|
||||
|
||||
[HttpPost("category/edit/{id}")]
|
||||
public async Task<IActionResult> EditCategory(string id, PortfolioCategory category)
|
||||
{
|
||||
if (!base.ModelState.IsValid)
|
||||
{
|
||||
return View(category);
|
||||
}
|
||||
category.Id = id;
|
||||
category.UpdatedAt = DateTime.UtcNow;
|
||||
category.Slug = _slugService.GenerateSlug(category.Name);
|
||||
await _pgService.UpdateAsync(_categoriesCollection, id, category);
|
||||
base.TempData["SuccessMessage"] = "Category updated successfully!";
|
||||
return RedirectToAction("Categories");
|
||||
}
|
||||
|
||||
[HttpPost("category/delete/{id}")]
|
||||
public async Task<IActionResult> DeleteCategory(string id)
|
||||
{
|
||||
await _pgService.DeleteAsync<PortfolioCategory>(_categoriesCollection, id);
|
||||
base.TempData["SuccessMessage"] = "Category deleted successfully!";
|
||||
return RedirectToAction("Categories");
|
||||
}
|
||||
|
||||
[HttpGet("projects")]
|
||||
public async Task<IActionResult> Projects(string? categoryId)
|
||||
{
|
||||
List<PortfolioProject> projects = await _pgService.GetAllAsync<PortfolioProject>(_projectsCollection);
|
||||
List<PortfolioCategory> source = await _pgService.GetAllAsync<PortfolioCategory>(_categoriesCollection);
|
||||
if (!string.IsNullOrEmpty(categoryId))
|
||||
{
|
||||
projects = projects.Where((PortfolioProject p) => p.CategoryId == categoryId).ToList();
|
||||
}
|
||||
base.ViewBag.Categories = source.Where((PortfolioCategory c) => c.IsActive).ToList();
|
||||
base.ViewBag.SelectedCategory = categoryId;
|
||||
return View(projects.OrderBy((PortfolioProject p) => p.DisplayOrder).ToList());
|
||||
}
|
||||
|
||||
[HttpGet("project/create")]
|
||||
public async Task<IActionResult> CreateProject()
|
||||
{
|
||||
List<PortfolioCategory> source = await _pgService.GetAllAsync<PortfolioCategory>(_categoriesCollection);
|
||||
base.ViewBag.Categories = source.Where((PortfolioCategory c) => c.IsActive).ToList();
|
||||
return View(new PortfolioProject());
|
||||
}
|
||||
|
||||
[HttpPost("project/create")]
|
||||
public async Task<IActionResult> CreateProject(PortfolioProject project)
|
||||
{
|
||||
if (!base.ModelState.IsValid)
|
||||
{
|
||||
List<PortfolioCategory> source = await _pgService.GetAllAsync<PortfolioCategory>(_categoriesCollection);
|
||||
base.ViewBag.Categories = source.Where((PortfolioCategory c) => c.IsActive).ToList();
|
||||
return View(project);
|
||||
}
|
||||
project.CreatedAt = DateTime.UtcNow;
|
||||
project.UpdatedAt = DateTime.UtcNow;
|
||||
await _pgService.InsertAsync(_projectsCollection, project);
|
||||
base.TempData["SuccessMessage"] = "Project created successfully!";
|
||||
return RedirectToAction("Projects");
|
||||
}
|
||||
|
||||
[HttpGet("project/edit/{id}")]
|
||||
public async Task<IActionResult> EditProject(string id)
|
||||
{
|
||||
PortfolioProject project = await _pgService.GetByIdAsync<PortfolioProject>(_projectsCollection, id);
|
||||
if (project == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
List<PortfolioCategory> source = await _pgService.GetAllAsync<PortfolioCategory>(_categoriesCollection);
|
||||
base.ViewBag.Categories = source.Where((PortfolioCategory c) => c.IsActive).ToList();
|
||||
return View(project);
|
||||
}
|
||||
|
||||
[HttpPost("project/edit/{id}")]
|
||||
public async Task<IActionResult> EditProject(string id, PortfolioProject project)
|
||||
{
|
||||
if (!base.ModelState.IsValid)
|
||||
{
|
||||
List<PortfolioCategory> source = await _pgService.GetAllAsync<PortfolioCategory>(_categoriesCollection);
|
||||
base.ViewBag.Categories = source.Where((PortfolioCategory c) => c.IsActive).ToList();
|
||||
return View(project);
|
||||
}
|
||||
project.Id = id;
|
||||
project.UpdatedAt = DateTime.UtcNow;
|
||||
await _pgService.UpdateAsync(_projectsCollection, id, project);
|
||||
base.TempData["SuccessMessage"] = "Project updated successfully!";
|
||||
return RedirectToAction("Projects");
|
||||
}
|
||||
|
||||
[HttpPost("project/delete/{id}")]
|
||||
public async Task<IActionResult> DeleteProject(string id)
|
||||
{
|
||||
await _pgService.DeleteAsync<PortfolioProject>(_projectsCollection, id);
|
||||
base.TempData["SuccessMessage"] = "Project deleted successfully!";
|
||||
return RedirectToAction("Projects");
|
||||
}
|
||||
}
|
||||
265
Controllers/AdminProductsController.cs
Normal file
265
Controllers/AdminProductsController.cs
Normal file
@@ -0,0 +1,265 @@
|
||||
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");
|
||||
}
|
||||
}
|
||||
55
Controllers/AdminSettingsController.cs
Normal file
55
Controllers/AdminSettingsController.cs
Normal file
@@ -0,0 +1,55 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using SkyArtShop.Models;
|
||||
using SkyArtShop.Services;
|
||||
|
||||
namespace SkyArtShop.Controllers;
|
||||
|
||||
[Route("admin/settings")]
|
||||
[Authorize(Roles = "Admin,MasterAdmin")]
|
||||
public class AdminSettingsController : Controller
|
||||
{
|
||||
private readonly PostgreSQLService _pgService;
|
||||
|
||||
private readonly string _settingsCollection = "SiteSettings";
|
||||
|
||||
public AdminSettingsController(PostgreSQLService pgService)
|
||||
{
|
||||
_pgService = pgService;
|
||||
}
|
||||
|
||||
[HttpGet("")]
|
||||
public async Task<IActionResult> Index()
|
||||
{
|
||||
SiteSettings settings = (await _pgService.GetAllAsync<SiteSettings>(_settingsCollection)).FirstOrDefault();
|
||||
if (settings == null)
|
||||
{
|
||||
settings = new SiteSettings();
|
||||
await _pgService.InsertAsync(_settingsCollection, settings);
|
||||
}
|
||||
return View(settings);
|
||||
}
|
||||
|
||||
[HttpPost("update")]
|
||||
public async Task<IActionResult> Update(SiteSettings settings)
|
||||
{
|
||||
if (!base.ModelState.IsValid)
|
||||
{
|
||||
return View("Index", settings);
|
||||
}
|
||||
settings.UpdatedAt = DateTime.UtcNow;
|
||||
if (string.IsNullOrEmpty(settings.Id))
|
||||
{
|
||||
await _pgService.InsertAsync(_settingsCollection, settings);
|
||||
}
|
||||
else
|
||||
{
|
||||
await _pgService.UpdateAsync(_settingsCollection, settings.Id, settings);
|
||||
}
|
||||
base.TempData["SuccessMessage"] = "Site settings updated successfully!";
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
}
|
||||
268
Controllers/AdminUploadController.cs
Normal file
268
Controllers/AdminUploadController.cs
Normal file
@@ -0,0 +1,268 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace SkyArtShop.Controllers;
|
||||
|
||||
[Route("admin/upload")]
|
||||
[Authorize(Roles = "Admin,MasterAdmin")]
|
||||
[IgnoreAntiforgeryToken]
|
||||
public class AdminUploadController : Controller
|
||||
{
|
||||
private readonly IWebHostEnvironment _environment;
|
||||
|
||||
public AdminUploadController(IWebHostEnvironment environment)
|
||||
{
|
||||
_environment = environment;
|
||||
}
|
||||
|
||||
[HttpGet("")]
|
||||
public IActionResult Index()
|
||||
{
|
||||
string path = Path.Combine(_environment.WebRootPath, "uploads", "images");
|
||||
List<string> model = new List<string>();
|
||||
if (Directory.Exists(path))
|
||||
{
|
||||
List<string> list = (from f in Directory.GetFiles(path)
|
||||
select "/uploads/images/" + Path.GetFileName(f) into f
|
||||
orderby f descending
|
||||
select f).ToList();
|
||||
model = list;
|
||||
}
|
||||
return View(model);
|
||||
}
|
||||
|
||||
[HttpPost("image")]
|
||||
public async Task<IActionResult> UploadImage(IFormFile file)
|
||||
{
|
||||
if (file == null || file.Length == 0L)
|
||||
{
|
||||
return Json(new
|
||||
{
|
||||
success = false,
|
||||
message = "No file uploaded"
|
||||
});
|
||||
}
|
||||
string[] source = new string[5] { ".jpg", ".jpeg", ".png", ".gif", ".webp" };
|
||||
string value = Path.GetExtension(file.FileName).ToLowerInvariant();
|
||||
if (!source.Contains(value))
|
||||
{
|
||||
return Json(new
|
||||
{
|
||||
success = false,
|
||||
message = "Invalid file type"
|
||||
});
|
||||
}
|
||||
try
|
||||
{
|
||||
string text = Path.Combine(_environment.WebRootPath, "uploads", "images");
|
||||
if (!Directory.Exists(text))
|
||||
{
|
||||
Directory.CreateDirectory(text);
|
||||
}
|
||||
string fileName = $"{Guid.NewGuid()}{value}";
|
||||
string path = Path.Combine(text, fileName);
|
||||
using FileStream stream = new FileStream(path, FileMode.Create);
|
||||
await file.CopyToAsync(stream);
|
||||
return Json(new
|
||||
{
|
||||
success = true,
|
||||
url = "/uploads/images/" + fileName
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Json(new
|
||||
{
|
||||
success = false,
|
||||
message = ex.Message
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPost("multiple")]
|
||||
public async Task<IActionResult> UploadMultiple(List<IFormFile> files)
|
||||
{
|
||||
List<string> uploadedUrls = new List<string>();
|
||||
foreach (IFormFile file in files)
|
||||
{
|
||||
if (file == null || file.Length == 0L)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
string value = Path.GetExtension(file.FileName).ToLowerInvariant();
|
||||
string[] source = new string[5] { ".jpg", ".jpeg", ".png", ".gif", ".webp" };
|
||||
if (source.Contains(value))
|
||||
{
|
||||
string text = Path.Combine(_environment.WebRootPath, "uploads", "images");
|
||||
if (!Directory.Exists(text))
|
||||
{
|
||||
Directory.CreateDirectory(text);
|
||||
}
|
||||
string fileName = $"{Guid.NewGuid()}{value}";
|
||||
string path = Path.Combine(text, fileName);
|
||||
using FileStream stream = new FileStream(path, FileMode.Create);
|
||||
await file.CopyToAsync(stream);
|
||||
uploadedUrls.Add("/uploads/images/" + fileName);
|
||||
}
|
||||
}
|
||||
return Json(new
|
||||
{
|
||||
success = true,
|
||||
urls = uploadedUrls
|
||||
});
|
||||
}
|
||||
|
||||
[HttpPost("delete")]
|
||||
public IActionResult DeleteImage([FromBody] string imageUrl)
|
||||
{
|
||||
try
|
||||
{
|
||||
string fileName = Path.GetFileName(imageUrl);
|
||||
string path = Path.Combine(_environment.WebRootPath, "uploads", "images", fileName);
|
||||
if (System.IO.File.Exists(path))
|
||||
{
|
||||
System.IO.File.Delete(path);
|
||||
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
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[HttpGet("list")]
|
||||
public IActionResult ListImages()
|
||||
{
|
||||
string uploadsPath = Path.Combine(_environment.WebRootPath, "uploads", "images");
|
||||
List<string> data = new List<string>();
|
||||
if (Directory.Exists(uploadsPath))
|
||||
{
|
||||
List<string> list = (from f in Directory.GetFiles(uploadsPath)
|
||||
select "/uploads/images/" + Path.GetFileName(f) into f
|
||||
orderby System.IO.File.GetCreationTime(Path.Combine(uploadsPath, Path.GetFileName(f))) descending
|
||||
select f).ToList();
|
||||
data = list;
|
||||
}
|
||||
return Json(data);
|
||||
}
|
||||
|
||||
[HttpPost("create-folder")]
|
||||
public IActionResult CreateFolder([FromBody] string folderName)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(folderName))
|
||||
{
|
||||
return Json(new
|
||||
{
|
||||
success = false,
|
||||
message = "Folder name cannot be empty"
|
||||
});
|
||||
}
|
||||
string text = string.Join("_", folderName.Split(Path.GetInvalidFileNameChars()));
|
||||
string path = Path.Combine(_environment.WebRootPath, "uploads", "images", text);
|
||||
if (Directory.Exists(path))
|
||||
{
|
||||
return Json(new
|
||||
{
|
||||
success = false,
|
||||
message = "Folder already exists"
|
||||
});
|
||||
}
|
||||
Directory.CreateDirectory(path);
|
||||
return Json(new
|
||||
{
|
||||
success = true,
|
||||
folderName = text
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Json(new
|
||||
{
|
||||
success = false,
|
||||
message = ex.Message
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPost("delete-folder")]
|
||||
public IActionResult DeleteFolder([FromBody] string folderPath)
|
||||
{
|
||||
try
|
||||
{
|
||||
string path = Path.Combine(_environment.WebRootPath, "uploads", "images", folderPath);
|
||||
if (!Directory.Exists(path))
|
||||
{
|
||||
return Json(new
|
||||
{
|
||||
success = false,
|
||||
message = "Folder not found"
|
||||
});
|
||||
}
|
||||
Directory.Delete(path, recursive: true);
|
||||
return Json(new
|
||||
{
|
||||
success = true
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Json(new
|
||||
{
|
||||
success = false,
|
||||
message = ex.Message
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[HttpGet("list-folders")]
|
||||
public IActionResult ListFolders()
|
||||
{
|
||||
try
|
||||
{
|
||||
string path = Path.Combine(_environment.WebRootPath, "uploads", "images");
|
||||
List<object> data = new List<object>();
|
||||
if (Directory.Exists(path))
|
||||
{
|
||||
var source = (from d in Directory.GetDirectories(path)
|
||||
select new
|
||||
{
|
||||
name = Path.GetFileName(d),
|
||||
path = Path.GetFileName(d),
|
||||
fileCount = Directory.GetFiles(d).Length
|
||||
}).ToList();
|
||||
data = source.Cast<object>().ToList();
|
||||
}
|
||||
return Json(data);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Json(new
|
||||
{
|
||||
success = false,
|
||||
message = ex.Message
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
160
Controllers/AdminUsersController.cs
Normal file
160
Controllers/AdminUsersController.cs
Normal file
@@ -0,0 +1,160 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using SkyArtShop.Models;
|
||||
using SkyArtShop.Services;
|
||||
|
||||
namespace SkyArtShop.Controllers;
|
||||
|
||||
[Authorize(Roles = "Admin,MasterAdmin")]
|
||||
[Route("admin/users")]
|
||||
public class AdminUsersController : Controller
|
||||
{
|
||||
private readonly PostgreSQLService _pgService;
|
||||
|
||||
private readonly PostgreAuthService _authService;
|
||||
|
||||
public AdminUsersController(PostgreSQLService pgService, PostgreAuthService authService)
|
||||
{
|
||||
_pgService = pgService;
|
||||
_authService = authService;
|
||||
}
|
||||
|
||||
[HttpGet("")]
|
||||
public async Task<IActionResult> Index()
|
||||
{
|
||||
return View((await _pgService.GetAllAsync<AdminUser>("AdminUsers")).OrderBy((AdminUser u) => u.CreatedAt).ToList());
|
||||
}
|
||||
|
||||
[HttpGet("create")]
|
||||
public IActionResult Create()
|
||||
{
|
||||
base.ViewBag.Roles = GetAvailableRoles();
|
||||
return View();
|
||||
}
|
||||
|
||||
[HttpPost("create")]
|
||||
public async Task<IActionResult> Create(AdminUser user, string password)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(password))
|
||||
{
|
||||
base.ModelState.AddModelError("", "Password is required");
|
||||
base.ViewBag.Roles = GetAvailableRoles();
|
||||
return View(user);
|
||||
}
|
||||
if (await _authService.GetUserByEmailAsync(user.Email) != null)
|
||||
{
|
||||
base.ModelState.AddModelError("", "Email already exists");
|
||||
base.ViewBag.Roles = GetAvailableRoles();
|
||||
return View(user);
|
||||
}
|
||||
AdminUser adminUser = await _authService.CreateUserAsync(user.Email, password, user.Name, user.Role);
|
||||
adminUser.Phone = user.Phone;
|
||||
adminUser.Notes = user.Notes;
|
||||
adminUser.Permissions = GetRolePermissions(user.Role);
|
||||
adminUser.CreatedBy = base.User.Identity?.Name ?? "System";
|
||||
adminUser.PasswordNeverExpires = user.PasswordNeverExpires;
|
||||
adminUser.PasswordExpiresAt = (user.PasswordNeverExpires ? ((DateTime?)null) : new DateTime?(DateTime.UtcNow.AddDays(90.0)));
|
||||
await _pgService.UpdateAsync("AdminUsers", adminUser.Id, adminUser);
|
||||
base.TempData["Success"] = "User " + user.Name + " created successfully! They can now login.";
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
|
||||
[HttpGet("edit/{id}")]
|
||||
public async Task<IActionResult> Edit(string id)
|
||||
{
|
||||
AdminUser adminUser = await _pgService.GetByIdAsync<AdminUser>("AdminUsers", id);
|
||||
if (adminUser == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
base.ViewBag.Roles = GetAvailableRoles();
|
||||
return View(adminUser);
|
||||
}
|
||||
|
||||
[HttpPost("edit/{id}")]
|
||||
public async Task<IActionResult> Edit(string id, AdminUser user, string? newPassword)
|
||||
{
|
||||
AdminUser adminUser = await _pgService.GetByIdAsync<AdminUser>("AdminUsers", id);
|
||||
if (adminUser == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
adminUser.Name = user.Name;
|
||||
adminUser.Email = user.Email;
|
||||
adminUser.Role = user.Role;
|
||||
adminUser.Phone = user.Phone;
|
||||
adminUser.Notes = user.Notes;
|
||||
adminUser.IsActive = user.IsActive;
|
||||
adminUser.Permissions = GetRolePermissions(user.Role);
|
||||
adminUser.PasswordNeverExpires = user.PasswordNeverExpires;
|
||||
adminUser.PasswordExpiresAt = (user.PasswordNeverExpires ? ((DateTime?)null) : new DateTime?(DateTime.UtcNow.AddDays(90.0)));
|
||||
if (!string.IsNullOrWhiteSpace(newPassword))
|
||||
{
|
||||
adminUser.PasswordHash = _authService.HashPassword(newPassword);
|
||||
}
|
||||
await _pgService.UpdateAsync("AdminUsers", id, adminUser);
|
||||
if (!string.IsNullOrWhiteSpace(newPassword))
|
||||
{
|
||||
base.TempData["Success"] = "User " + user.Name + " and password updated successfully!";
|
||||
}
|
||||
else
|
||||
{
|
||||
base.TempData["Success"] = "User " + user.Name + " updated successfully!";
|
||||
}
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
|
||||
[HttpPost("delete/{id}")]
|
||||
public async Task<IActionResult> Delete(string id)
|
||||
{
|
||||
AdminUser user = await _pgService.GetByIdAsync<AdminUser>("AdminUsers", id);
|
||||
if (user == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
if (user.Role == "MasterAdmin")
|
||||
{
|
||||
base.TempData["Error"] = "Cannot delete Master Admin!";
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
await _pgService.DeleteAsync<AdminUser>("AdminUsers", id);
|
||||
base.TempData["Success"] = "User " + user.Name + " deleted successfully!";
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
|
||||
[HttpGet("view/{id}")]
|
||||
public async Task<IActionResult> ViewUser(string id)
|
||||
{
|
||||
AdminUser adminUser = await _pgService.GetByIdAsync<AdminUser>("AdminUsers", id);
|
||||
if (adminUser == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
return View("View", adminUser);
|
||||
}
|
||||
|
||||
private List<string> GetAvailableRoles()
|
||||
{
|
||||
return new List<string> { "MasterAdmin", "Admin", "Cashier", "Accountant" };
|
||||
}
|
||||
|
||||
private List<string> GetRolePermissions(string role)
|
||||
{
|
||||
return role switch
|
||||
{
|
||||
"MasterAdmin" => new List<string>
|
||||
{
|
||||
"manage_users", "manage_products", "manage_orders", "manage_content", "manage_settings", "view_reports", "manage_finances", "manage_inventory", "manage_customers", "manage_blog",
|
||||
"manage_portfolio", "manage_pages"
|
||||
},
|
||||
"Admin" => new List<string> { "manage_products", "manage_orders", "manage_content", "view_reports", "manage_inventory", "manage_customers", "manage_blog", "manage_portfolio", "manage_pages" },
|
||||
"Cashier" => new List<string> { "view_products", "manage_orders", "view_customers", "process_payments" },
|
||||
"Accountant" => new List<string> { "view_products", "view_orders", "view_reports", "manage_finances", "view_customers", "export_data" },
|
||||
_ => new List<string>(),
|
||||
};
|
||||
}
|
||||
}
|
||||
75
Controllers/ApiUploadController.cs
Normal file
75
Controllers/ApiUploadController.cs
Normal file
@@ -0,0 +1,75 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace SkyArtShop.Controllers;
|
||||
|
||||
[Route("api/upload")]
|
||||
[Authorize(Roles = "Admin")]
|
||||
public class ApiUploadController : Controller
|
||||
{
|
||||
private readonly IWebHostEnvironment _environment;
|
||||
|
||||
public ApiUploadController(IWebHostEnvironment environment)
|
||||
{
|
||||
_environment = environment;
|
||||
}
|
||||
|
||||
[HttpPost("image")]
|
||||
public async Task<IActionResult> UploadImage(IFormFile image)
|
||||
{
|
||||
if (image == null || image.Length == 0L)
|
||||
{
|
||||
return Json(new
|
||||
{
|
||||
success = false,
|
||||
message = "No file uploaded"
|
||||
});
|
||||
}
|
||||
string[] source = new string[5] { ".jpg", ".jpeg", ".png", ".gif", ".webp" };
|
||||
string value = Path.GetExtension(image.FileName).ToLowerInvariant();
|
||||
if (!source.Contains(value))
|
||||
{
|
||||
return Json(new
|
||||
{
|
||||
success = false,
|
||||
message = "Invalid file type. Only images are allowed."
|
||||
});
|
||||
}
|
||||
try
|
||||
{
|
||||
string text = Path.Combine(_environment.WebRootPath, "uploads", "images");
|
||||
if (!Directory.Exists(text))
|
||||
{
|
||||
Directory.CreateDirectory(text);
|
||||
}
|
||||
string fileName = $"{Guid.NewGuid()}{value}";
|
||||
string path = Path.Combine(text, fileName);
|
||||
using (FileStream stream = new FileStream(path, FileMode.Create))
|
||||
{
|
||||
await image.CopyToAsync(stream);
|
||||
}
|
||||
string text2 = "/uploads/images/" + fileName;
|
||||
Console.WriteLine("[API-UPLOAD] Image uploaded successfully: " + text2);
|
||||
return Json(new
|
||||
{
|
||||
success = true,
|
||||
imageUrl = text2
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine("[API-UPLOAD] Upload failed: " + ex.Message);
|
||||
return Json(new
|
||||
{
|
||||
success = false,
|
||||
message = "Upload failed: " + ex.Message
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
39
Controllers/BlogController.cs
Normal file
39
Controllers/BlogController.cs
Normal file
@@ -0,0 +1,39 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using SkyArtShop.Models;
|
||||
using SkyArtShop.Services;
|
||||
|
||||
namespace SkyArtShop.Controllers;
|
||||
|
||||
public class BlogController : Controller
|
||||
{
|
||||
private readonly PostgreSQLService _pgService;
|
||||
|
||||
private readonly string _blogCollection = "BlogPosts";
|
||||
|
||||
public BlogController(PostgreSQLService pgService)
|
||||
{
|
||||
_pgService = pgService;
|
||||
}
|
||||
|
||||
public async Task<IActionResult> Index()
|
||||
{
|
||||
List<BlogPost> model = (from p in await _pgService.GetAllAsync<BlogPost>(_blogCollection)
|
||||
where p.IsPublished
|
||||
orderby p.PublishedDate descending
|
||||
select p).ToList();
|
||||
return View(model);
|
||||
}
|
||||
|
||||
public async Task<IActionResult> Post(string slug)
|
||||
{
|
||||
BlogPost blogPost = (await _pgService.GetAllAsync<BlogPost>(_blogCollection)).FirstOrDefault((BlogPost p) => p.Slug == slug && p.IsPublished);
|
||||
if (blogPost == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
return View(blogPost);
|
||||
}
|
||||
}
|
||||
29
Controllers/ContactController.cs
Normal file
29
Controllers/ContactController.cs
Normal file
@@ -0,0 +1,29 @@
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using SkyArtShop.Models;
|
||||
using SkyArtShop.Services;
|
||||
|
||||
namespace SkyArtShop.Controllers;
|
||||
|
||||
public class ContactController : Controller
|
||||
{
|
||||
private readonly PostgreSQLService _pgService;
|
||||
|
||||
public ContactController(PostgreSQLService pgService)
|
||||
{
|
||||
_pgService = pgService;
|
||||
}
|
||||
|
||||
public async Task<IActionResult> Index()
|
||||
{
|
||||
SiteSettings model = (await _pgService.GetSiteSettingsAsync()) ?? new SiteSettings();
|
||||
return View(model);
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public IActionResult Submit(string name, string email, string phone, string subject, string message)
|
||||
{
|
||||
base.TempData["Success"] = "Thank you! Your message has been sent. We'll get back to you soon.";
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
}
|
||||
34
Controllers/DiagnosticsController.cs
Normal file
34
Controllers/DiagnosticsController.cs
Normal file
@@ -0,0 +1,34 @@
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using SkyArtShop.Models;
|
||||
using SkyArtShop.Services;
|
||||
|
||||
namespace SkyArtShop.Controllers;
|
||||
|
||||
[Route("diagnostics")]
|
||||
public class DiagnosticsController : Controller
|
||||
{
|
||||
private readonly MongoDBService _mongoService;
|
||||
|
||||
public DiagnosticsController(MongoDBService mongoService)
|
||||
{
|
||||
_mongoService = mongoService;
|
||||
}
|
||||
|
||||
[HttpGet("products")]
|
||||
public async Task<IActionResult> Products()
|
||||
{
|
||||
var data = (await _mongoService.GetAllAsync<Product>("Products")).Select((Product p) => new
|
||||
{
|
||||
Id = p.Id,
|
||||
Name = p.Name,
|
||||
ImageUrl = p.ImageUrl,
|
||||
ImagesCount = (p.Images?.Count ?? 0),
|
||||
FirstImage = p.Images?.FirstOrDefault(),
|
||||
HasImageUrl = !string.IsNullOrEmpty(p.ImageUrl),
|
||||
HasImages = (p.Images != null && p.Images.Any())
|
||||
}).ToList();
|
||||
return Json(data);
|
||||
}
|
||||
}
|
||||
67
Controllers/HomeController.cs
Normal file
67
Controllers/HomeController.cs
Normal file
@@ -0,0 +1,67 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using SkyArtShop.Models;
|
||||
using SkyArtShop.Services;
|
||||
|
||||
namespace SkyArtShop.Controllers;
|
||||
|
||||
public class HomeController : Controller
|
||||
{
|
||||
private readonly PostgreSQLService _pgService;
|
||||
|
||||
private readonly string _productsCollection = "Products";
|
||||
|
||||
private readonly string _sectionsCollection = "HomepageSections";
|
||||
|
||||
public HomeController(PostgreSQLService pgService)
|
||||
{
|
||||
_pgService = pgService;
|
||||
}
|
||||
|
||||
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
|
||||
public async Task<IActionResult> Index()
|
||||
{
|
||||
SiteSettings settings = await GetSiteSettings();
|
||||
List<Product> topProducts = await GetTopSellerProducts();
|
||||
List<HomepageSection> list = await GetHomepageSections();
|
||||
base.ViewBag.Settings = settings;
|
||||
base.ViewBag.TopProducts = topProducts;
|
||||
base.ViewBag.Sections = list;
|
||||
return View();
|
||||
}
|
||||
|
||||
private async Task<SiteSettings> GetSiteSettings()
|
||||
{
|
||||
return (await _pgService.GetSiteSettingsAsync()) ?? new SiteSettings();
|
||||
}
|
||||
|
||||
private async Task<List<Product>> GetTopSellerProducts()
|
||||
{
|
||||
return (await _pgService.GetAllAsync<Product>(_productsCollection)).Where((Product p) => p.IsTopSeller && p.IsActive).Take(4).ToList();
|
||||
}
|
||||
|
||||
private async Task<List<HomepageSection>> GetHomepageSections()
|
||||
{
|
||||
List<HomepageSection> list = await _pgService.GetAllAsync<HomepageSection>(_sectionsCollection);
|
||||
Console.WriteLine($"Total sections from DB: {list.Count}");
|
||||
List<HomepageSection> list2 = (from s in list
|
||||
where s.IsActive
|
||||
orderby s.DisplayOrder
|
||||
select s).ToList();
|
||||
Console.WriteLine($"Active sections: {list2.Count}");
|
||||
foreach (HomepageSection item in list2)
|
||||
{
|
||||
Console.WriteLine($"Section: {item.Title} | Type: {item.SectionType} | Order: {item.DisplayOrder} | Active: {item.IsActive}");
|
||||
}
|
||||
return list2;
|
||||
}
|
||||
|
||||
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
|
||||
public IActionResult Error()
|
||||
{
|
||||
return View();
|
||||
}
|
||||
}
|
||||
29
Controllers/PageController.cs
Normal file
29
Controllers/PageController.cs
Normal file
@@ -0,0 +1,29 @@
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using SkyArtShop.Models;
|
||||
using SkyArtShop.Services;
|
||||
|
||||
namespace SkyArtShop.Controllers;
|
||||
|
||||
[Route("page")]
|
||||
public class PageController : Controller
|
||||
{
|
||||
private readonly PostgreSQLService _pgService;
|
||||
|
||||
public PageController(PostgreSQLService pgService)
|
||||
{
|
||||
_pgService = pgService;
|
||||
}
|
||||
|
||||
[HttpGet("{slug}")]
|
||||
public async Task<IActionResult> Index(string slug)
|
||||
{
|
||||
Page page = (await _pgService.GetAllAsync<Page>("Pages")).FirstOrDefault((Page p) => p.PageSlug == slug && p.IsActive);
|
||||
if (page == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
return View("View", page);
|
||||
}
|
||||
}
|
||||
56
Controllers/PortfolioController.cs
Normal file
56
Controllers/PortfolioController.cs
Normal file
@@ -0,0 +1,56 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using SkyArtShop.Models;
|
||||
using SkyArtShop.Services;
|
||||
|
||||
namespace SkyArtShop.Controllers;
|
||||
|
||||
public class PortfolioController : Controller
|
||||
{
|
||||
private readonly PostgreSQLService _pgService;
|
||||
|
||||
private readonly string _categoriesCollection = "PortfolioCategories";
|
||||
|
||||
private readonly string _projectsCollection = "PortfolioProjects";
|
||||
|
||||
public PortfolioController(PostgreSQLService pgService)
|
||||
{
|
||||
_pgService = pgService;
|
||||
}
|
||||
|
||||
public async Task<IActionResult> Index()
|
||||
{
|
||||
List<PortfolioCategory> model = (from c in await _pgService.GetAllAsync<PortfolioCategory>(_categoriesCollection)
|
||||
where c.IsActive
|
||||
orderby c.DisplayOrder
|
||||
select c).ToList();
|
||||
return View(model);
|
||||
}
|
||||
|
||||
public async Task<IActionResult> Category(string slug)
|
||||
{
|
||||
PortfolioCategory category = (await _pgService.GetAllAsync<PortfolioCategory>(_categoriesCollection)).FirstOrDefault((PortfolioCategory c) => c.Slug == slug && c.IsActive);
|
||||
if (category == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
List<PortfolioProject> model = (from p in await _pgService.GetAllAsync<PortfolioProject>(_projectsCollection)
|
||||
where p.CategoryId == category.Id && p.IsActive
|
||||
orderby p.DisplayOrder
|
||||
select p).ToList();
|
||||
base.ViewBag.Category = category;
|
||||
return View(model);
|
||||
}
|
||||
|
||||
public async Task<IActionResult> Project(string id)
|
||||
{
|
||||
PortfolioProject portfolioProject = await _pgService.GetByIdAsync<PortfolioProject>(_projectsCollection, id);
|
||||
if (portfolioProject == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
return View(portfolioProject);
|
||||
}
|
||||
}
|
||||
72
Controllers/ShopController.cs
Normal file
72
Controllers/ShopController.cs
Normal file
@@ -0,0 +1,72 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using SkyArtShop.Models;
|
||||
using SkyArtShop.Services;
|
||||
|
||||
namespace SkyArtShop.Controllers;
|
||||
|
||||
public class ShopController : Controller
|
||||
{
|
||||
private readonly PostgreSQLService _pgService;
|
||||
|
||||
private readonly string _productsCollection = "Products";
|
||||
|
||||
public ShopController(PostgreSQLService pgService)
|
||||
{
|
||||
_pgService = pgService;
|
||||
}
|
||||
|
||||
public async Task<IActionResult> Index(string? category, string? sort)
|
||||
{
|
||||
List<Product> source = (await _pgService.GetAllAsync<Product>(_productsCollection)).Where((Product p) => p.IsActive).ToList();
|
||||
if (!string.IsNullOrEmpty(category) && category != "all")
|
||||
{
|
||||
source = source.Where((Product p) => p.Category == category).ToList();
|
||||
}
|
||||
source = sort switch
|
||||
{
|
||||
"price-low" => source.OrderBy((Product p) => p.Price).ToList(),
|
||||
"price-high" => source.OrderByDescending((Product p) => p.Price).ToList(),
|
||||
"newest" => source.OrderByDescending((Product p) => p.CreatedAt).ToList(),
|
||||
_ => source.OrderByDescending((Product p) => p.IsFeatured).ToList(),
|
||||
};
|
||||
base.ViewBag.SelectedCategory = category ?? "all";
|
||||
base.ViewBag.SelectedSort = sort ?? "featured";
|
||||
base.ViewBag.Categories = source.Select((Product p) => p.Category).Distinct().ToList();
|
||||
return View(source);
|
||||
}
|
||||
|
||||
public async Task<IActionResult> Product(string id)
|
||||
{
|
||||
Product product = await _pgService.GetByIdAsync<Product>(_productsCollection, id);
|
||||
if (product == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
Console.WriteLine("[SHOP] Product ID: " + id);
|
||||
Console.WriteLine("[SHOP] Product Name: " + product.Name);
|
||||
Console.WriteLine($"[SHOP] Colors Count: {product.Colors?.Count ?? 0}");
|
||||
if (product.Colors != null && product.Colors.Any())
|
||||
{
|
||||
Console.WriteLine("[SHOP] Colors: " + string.Join(", ", product.Colors));
|
||||
}
|
||||
Console.WriteLine("[SHOP] Legacy Color: " + (product.Color ?? "null"));
|
||||
_ = base.HttpContext.Session.Id;
|
||||
if (base.HttpContext.Connection.RemoteIpAddress?.ToString() == null)
|
||||
{
|
||||
}
|
||||
List<Product> source = await _pgService.GetAllAsync<Product>(_productsCollection);
|
||||
List<ProductView> source2 = new List<ProductView>();
|
||||
Dictionary<string, int> productViewCounts = (from v in source2
|
||||
group v by v.ProductId).ToDictionary((IGrouping<string, ProductView> g) => g.Key, (IGrouping<string, ProductView> g) => g.Count());
|
||||
List<Product> list = (from p in source
|
||||
where p.IsActive && p.Id != id
|
||||
orderby ((p.Category == product.Category) ? 100 : 0) + (productViewCounts.ContainsKey(p.Id ?? "") ? productViewCounts[p.Id ?? ""] : 0) + (p.IsFeatured ? 50 : 0) + p.UnitsSold * 2 descending
|
||||
select p).Take(4).ToList();
|
||||
base.ViewBag.RelatedProducts = list;
|
||||
return View(product);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user