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:
61
Sky_Art_shop/Controllers/AboutController.cs
Normal file
61
Sky_Art_shop/Controllers/AboutController.cs
Normal file
@@ -0,0 +1,61 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using SkyArtShop.Models;
|
||||
using SkyArtShop.Services;
|
||||
|
||||
namespace SkyArtShop.Controllers
|
||||
{
|
||||
public class AboutController : Controller
|
||||
{
|
||||
private readonly MongoDBService _mongoService;
|
||||
private readonly string _pagesCollection = "Pages";
|
||||
|
||||
public AboutController(MongoDBService mongoService)
|
||||
{
|
||||
_mongoService = mongoService;
|
||||
}
|
||||
|
||||
public async Task<IActionResult> Index()
|
||||
{
|
||||
var pages = await _mongoService.GetAllAsync<Page>(_pagesCollection);
|
||||
var aboutPage = pages.FirstOrDefault(p => p.PageSlug == "about" && p.IsActive);
|
||||
|
||||
Console.WriteLine($"[ABOUT] Found About page: {aboutPage != null}");
|
||||
if (aboutPage != null)
|
||||
{
|
||||
Console.WriteLine($"[ABOUT] Title: {aboutPage.Title}");
|
||||
Console.WriteLine($"[ABOUT] Content length: {aboutPage.Content?.Length ?? 0}");
|
||||
Console.WriteLine($"[ABOUT] Image Gallery Count: {aboutPage.ImageGallery?.Count ?? 0}");
|
||||
Console.WriteLine($"[ABOUT] Team Members Count: {aboutPage.TeamMembers?.Count ?? 0}");
|
||||
|
||||
if (aboutPage.ImageGallery != null && aboutPage.ImageGallery.Any())
|
||||
{
|
||||
Console.WriteLine($"[ABOUT] Gallery Images: {string.Join(", ", aboutPage.ImageGallery)}");
|
||||
}
|
||||
|
||||
if (aboutPage.TeamMembers != null && aboutPage.TeamMembers.Any())
|
||||
{
|
||||
foreach (var member in aboutPage.TeamMembers)
|
||||
{
|
||||
Console.WriteLine($"[ABOUT] Team: {member.Name} ({member.Role}) - Photo: {member.PhotoUrl}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (aboutPage == null)
|
||||
{
|
||||
aboutPage = 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(aboutPage);
|
||||
}
|
||||
}
|
||||
}
|
||||
86
Sky_Art_shop/Controllers/AdminBlogController.cs
Normal file
86
Sky_Art_shop/Controllers/AdminBlogController.cs
Normal file
@@ -0,0 +1,86 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using SkyArtShop.Models;
|
||||
using SkyArtShop.Services;
|
||||
|
||||
namespace SkyArtShop.Controllers
|
||||
{
|
||||
[Route("admin/blog")]
|
||||
[Authorize(Roles="Admin")]
|
||||
public class AdminBlogController : Controller
|
||||
{
|
||||
private readonly MongoDBService _mongoService;
|
||||
private readonly SlugService _slugService;
|
||||
private readonly string _blogCollection = "BlogPosts";
|
||||
|
||||
public AdminBlogController(MongoDBService mongoService, SlugService slugService)
|
||||
{
|
||||
_mongoService = mongoService;
|
||||
_slugService = slugService;
|
||||
}
|
||||
|
||||
[HttpGet("")]
|
||||
public async Task<IActionResult> Index()
|
||||
{
|
||||
var posts = await _mongoService.GetAllAsync<BlogPost>(_blogCollection);
|
||||
return View(posts.OrderByDescending(p => p.CreatedAt).ToList());
|
||||
}
|
||||
|
||||
[HttpGet("create")]
|
||||
public IActionResult Create() => View(new BlogPost());
|
||||
|
||||
[HttpPost("create")]
|
||||
public async Task<IActionResult> Create(BlogPost post)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return View(post);
|
||||
}
|
||||
|
||||
post.CreatedAt = DateTime.UtcNow;
|
||||
post.UpdatedAt = DateTime.UtcNow;
|
||||
post.PublishedDate = DateTime.UtcNow;
|
||||
post.Slug = _slugService.GenerateSlug(post.Title);
|
||||
await _mongoService.InsertAsync(_blogCollection, post);
|
||||
TempData["SuccessMessage"] = "Blog post created successfully!";
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
|
||||
[HttpGet("edit/{id}")]
|
||||
public async Task<IActionResult> Edit(string id)
|
||||
{
|
||||
var post = await _mongoService.GetByIdAsync<BlogPost>(_blogCollection, id);
|
||||
if (post == null) return NotFound();
|
||||
return View(post);
|
||||
}
|
||||
|
||||
[HttpPost("edit/{id}")]
|
||||
public async Task<IActionResult> Edit(string id, BlogPost post)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return View(post);
|
||||
}
|
||||
|
||||
post.Id = id;
|
||||
post.UpdatedAt = DateTime.UtcNow;
|
||||
post.Slug = _slugService.GenerateSlug(post.Title);
|
||||
await _mongoService.UpdateAsync(_blogCollection, id, post);
|
||||
TempData["SuccessMessage"] = "Blog post updated successfully!";
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
|
||||
[HttpPost("delete/{id}")]
|
||||
public async Task<IActionResult> Delete(string id)
|
||||
{
|
||||
await _mongoService.DeleteAsync<BlogPost>(_blogCollection, id);
|
||||
TempData["SuccessMessage"] = "Blog post deleted successfully!";
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
|
||||
private string GenerateSlug(string text)
|
||||
{
|
||||
return _slugService.GenerateSlug(text);
|
||||
}
|
||||
}
|
||||
}
|
||||
85
Sky_Art_shop/Controllers/AdminController.cs
Normal file
85
Sky_Art_shop/Controllers/AdminController.cs
Normal file
@@ -0,0 +1,85 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using SkyArtShop.Models;
|
||||
using SkyArtShop.Services;
|
||||
|
||||
namespace SkyArtShop.Controllers
|
||||
{
|
||||
[Route("admin")]
|
||||
[Authorize(Roles = "Admin")]
|
||||
public class AdminController : Controller
|
||||
{
|
||||
private readonly MongoDBService _mongoService;
|
||||
private readonly SignInManager<SkyArtShop.Data.ApplicationUser> _signInManager;
|
||||
private readonly UserManager<SkyArtShop.Data.ApplicationUser> _userManager;
|
||||
|
||||
public AdminController(MongoDBService mongoService,
|
||||
SignInManager<SkyArtShop.Data.ApplicationUser> signInManager,
|
||||
UserManager<SkyArtShop.Data.ApplicationUser> userManager)
|
||||
{
|
||||
_mongoService = mongoService;
|
||||
_signInManager = signInManager;
|
||||
_userManager = userManager;
|
||||
}
|
||||
|
||||
[HttpGet("login")]
|
||||
[AllowAnonymous]
|
||||
public IActionResult Login()
|
||||
{
|
||||
if (User.Identity?.IsAuthenticated == true)
|
||||
{
|
||||
return RedirectToAction("Dashboard");
|
||||
}
|
||||
return View();
|
||||
}
|
||||
|
||||
[HttpPost("login")]
|
||||
[AllowAnonymous]
|
||||
public async Task<IActionResult> Login(string email, string password)
|
||||
{
|
||||
var user = await _userManager.FindByEmailAsync(email);
|
||||
if (user == null)
|
||||
{
|
||||
ViewBag.Error = "Invalid email or password";
|
||||
return View();
|
||||
}
|
||||
var result = await _signInManager.PasswordSignInAsync(user, password, true, false);
|
||||
if (!result.Succeeded)
|
||||
{
|
||||
ViewBag.Error = "Invalid email or password";
|
||||
return View();
|
||||
}
|
||||
return RedirectToAction("Dashboard");
|
||||
}
|
||||
|
||||
[HttpGet("logout")]
|
||||
public async Task<IActionResult> Logout()
|
||||
{
|
||||
await _signInManager.SignOutAsync();
|
||||
return RedirectToAction("Login");
|
||||
}
|
||||
|
||||
[HttpGet("dashboard")]
|
||||
public async Task<IActionResult> Dashboard()
|
||||
{
|
||||
var products = await _mongoService.GetAllAsync<Product>("Products");
|
||||
var projects = await _mongoService.GetAllAsync<PortfolioProject>("PortfolioProjects");
|
||||
var blogPosts = await _mongoService.GetAllAsync<BlogPost>("BlogPosts");
|
||||
var pages = await _mongoService.GetAllAsync<Page>("Pages");
|
||||
var settings = (await _mongoService.GetAllAsync<SiteSettings>("SiteSettings")).FirstOrDefault();
|
||||
|
||||
ViewBag.ProductCount = products.Count;
|
||||
ViewBag.ProjectCount = projects.Count;
|
||||
ViewBag.BlogCount = blogPosts.Count;
|
||||
ViewBag.PageCount = pages.Count;
|
||||
ViewBag.SiteName = settings?.SiteName ?? "Sky Art Shop";
|
||||
ViewBag.AdminEmail = User.Identity?.Name;
|
||||
|
||||
return View();
|
||||
}
|
||||
|
||||
[HttpGet("")]
|
||||
public IActionResult Index() => RedirectToAction("Dashboard");
|
||||
}
|
||||
}
|
||||
225
Sky_Art_shop/Controllers/AdminHomepageController.cs
Normal file
225
Sky_Art_shop/Controllers/AdminHomepageController.cs
Normal file
@@ -0,0 +1,225 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using SkyArtShop.Models;
|
||||
using SkyArtShop.Services;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace SkyArtShop.Controllers
|
||||
{
|
||||
[Route("admin/homepage")]
|
||||
[Authorize(Roles = "Admin")]
|
||||
public class AdminHomepageController : Controller
|
||||
{
|
||||
private readonly MongoDBService _mongoService;
|
||||
private readonly IWebHostEnvironment _environment;
|
||||
private readonly string _sectionsCollection = "HomepageSections";
|
||||
private readonly string _settingsCollection = "SiteSettings";
|
||||
|
||||
public AdminHomepageController(MongoDBService mongoService, IWebHostEnvironment environment)
|
||||
{
|
||||
_mongoService = mongoService;
|
||||
_environment = environment;
|
||||
}
|
||||
|
||||
[HttpGet("")]
|
||||
public async Task<IActionResult> Index()
|
||||
{
|
||||
var sections = await _mongoService.GetAllAsync<HomepageSection>(_sectionsCollection);
|
||||
sections = sections.OrderBy(s => s.DisplayOrder).ToList();
|
||||
|
||||
var settingsList = await _mongoService.GetAllAsync<SiteSettings>(_settingsCollection);
|
||||
var settings = settingsList.FirstOrDefault() ?? new SiteSettings();
|
||||
|
||||
ViewBag.Settings = settings;
|
||||
return View(sections);
|
||||
}
|
||||
|
||||
[HttpGet("section/{id}")]
|
||||
public async Task<IActionResult> EditSection(string id)
|
||||
{
|
||||
var section = await _mongoService.GetByIdAsync<HomepageSection>(_sectionsCollection, id);
|
||||
if (section == null)
|
||||
{
|
||||
TempData["ErrorMessage"] = "Section not found.";
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
return View(section);
|
||||
}
|
||||
|
||||
[HttpPost("section/update")]
|
||||
public async Task<IActionResult> UpdateSection(HomepageSection section, IFormFile? imageFile)
|
||||
{
|
||||
// Remove Content validation error since it's optional for some section types
|
||||
ModelState.Remove("Content");
|
||||
ModelState.Remove("AdditionalData");
|
||||
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
foreach (var error in ModelState.Values.SelectMany(v => v.Errors))
|
||||
{
|
||||
Console.WriteLine($"ModelState Error: {error.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}");
|
||||
|
||||
// Get existing section to preserve data
|
||||
var existingSection = await _mongoService.GetByIdAsync<HomepageSection>(_sectionsCollection, section.Id!);
|
||||
if (existingSection == null)
|
||||
{
|
||||
Console.WriteLine($"ERROR: Section with ID {section.Id} not found!");
|
||||
TempData["ErrorMessage"] = "Section not found.";
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
|
||||
Console.WriteLine($"Found existing section: {existingSection.Title}");
|
||||
|
||||
// Update fields
|
||||
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;
|
||||
|
||||
// Handle image upload
|
||||
if (imageFile != null && imageFile.Length > 0)
|
||||
{
|
||||
var uploadsFolder = Path.Combine(_environment.WebRootPath, "uploads", "images");
|
||||
Directory.CreateDirectory(uploadsFolder);
|
||||
|
||||
var uniqueFileName = $"{Guid.NewGuid()}_{imageFile.FileName}";
|
||||
var filePath = Path.Combine(uploadsFolder, uniqueFileName);
|
||||
|
||||
using (var fileStream = new FileStream(filePath, FileMode.Create))
|
||||
{
|
||||
await imageFile.CopyToAsync(fileStream);
|
||||
}
|
||||
|
||||
existingSection.ImageUrl = $"/uploads/images/{uniqueFileName}";
|
||||
Console.WriteLine($"New image uploaded: {existingSection.ImageUrl}");
|
||||
}
|
||||
|
||||
await _mongoService.UpdateAsync(_sectionsCollection, existingSection.Id!, existingSection);
|
||||
Console.WriteLine($"Section updated successfully in database");
|
||||
|
||||
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)
|
||||
{
|
||||
// Remove Content validation error since it's optional for some section types
|
||||
ModelState.Remove("Content");
|
||||
ModelState.Remove("AdditionalData");
|
||||
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return View(section);
|
||||
}
|
||||
|
||||
// Handle image upload
|
||||
if (imageFile != null && imageFile.Length > 0)
|
||||
{
|
||||
var uploadsFolder = Path.Combine(_environment.WebRootPath, "uploads", "images");
|
||||
Directory.CreateDirectory(uploadsFolder);
|
||||
|
||||
var uniqueFileName = $"{Guid.NewGuid()}_{imageFile.FileName}";
|
||||
var filePath = Path.Combine(uploadsFolder, uniqueFileName);
|
||||
|
||||
using (var fileStream = new FileStream(filePath, FileMode.Create))
|
||||
{
|
||||
await imageFile.CopyToAsync(fileStream);
|
||||
}
|
||||
|
||||
section.ImageUrl = $"/uploads/images/{uniqueFileName}";
|
||||
}
|
||||
|
||||
// Get the highest display order and add 1
|
||||
var allSections = await _mongoService.GetAllAsync<HomepageSection>(_sectionsCollection);
|
||||
section.DisplayOrder = allSections.Any() ? allSections.Max(s => s.DisplayOrder) + 1 : 0;
|
||||
|
||||
section.CreatedAt = DateTime.UtcNow;
|
||||
section.UpdatedAt = DateTime.UtcNow;
|
||||
|
||||
await _mongoService.InsertAsync(_sectionsCollection, section);
|
||||
|
||||
TempData["SuccessMessage"] = "Section created successfully!";
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
|
||||
[HttpPost("section/delete/{id}")]
|
||||
public async Task<IActionResult> DeleteSection(string id)
|
||||
{
|
||||
await _mongoService.DeleteAsync<HomepageSection>(_sectionsCollection, id);
|
||||
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++)
|
||||
{
|
||||
var section = await _mongoService.GetByIdAsync<HomepageSection>(_sectionsCollection, sectionIds[i]);
|
||||
if (section != null)
|
||||
{
|
||||
section.DisplayOrder = i;
|
||||
section.UpdatedAt = DateTime.UtcNow;
|
||||
await _mongoService.UpdateAsync(_sectionsCollection, section.Id!, section);
|
||||
}
|
||||
}
|
||||
return Json(new { success = true });
|
||||
}
|
||||
|
||||
[HttpPost("section/toggle/{id}")]
|
||||
public async Task<IActionResult> ToggleSection(string id)
|
||||
{
|
||||
var section = await _mongoService.GetByIdAsync<HomepageSection>(_sectionsCollection, id);
|
||||
if (section != null)
|
||||
{
|
||||
section.IsActive = !section.IsActive;
|
||||
section.UpdatedAt = DateTime.UtcNow;
|
||||
await _mongoService.UpdateAsync(_sectionsCollection, section.Id!, section);
|
||||
TempData["SuccessMessage"] = $"Section {(section.IsActive ? "activated" : "deactivated")} successfully!";
|
||||
}
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
|
||||
[HttpPost("footer/update")]
|
||||
public async Task<IActionResult> UpdateFooter(string footerText)
|
||||
{
|
||||
var settingsList = await _mongoService.GetAllAsync<SiteSettings>(_settingsCollection);
|
||||
SiteSettings? settings = settingsList.FirstOrDefault();
|
||||
|
||||
if (settings == null)
|
||||
{
|
||||
settings = new SiteSettings { FooterText = footerText };
|
||||
await _mongoService.InsertAsync(_settingsCollection, settings);
|
||||
}
|
||||
else
|
||||
{
|
||||
settings.FooterText = footerText;
|
||||
settings.UpdatedAt = DateTime.UtcNow;
|
||||
await _mongoService.UpdateAsync(_settingsCollection, settings.Id!, settings);
|
||||
}
|
||||
|
||||
TempData["SuccessMessage"] = "Footer updated successfully!";
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
}
|
||||
}
|
||||
113
Sky_Art_shop/Controllers/AdminMenuController.cs
Normal file
113
Sky_Art_shop/Controllers/AdminMenuController.cs
Normal file
@@ -0,0 +1,113 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using SkyArtShop.Models;
|
||||
using SkyArtShop.Services;
|
||||
|
||||
namespace SkyArtShop.Controllers
|
||||
{
|
||||
[Route("admin/menu")]
|
||||
[Authorize(Roles = "Admin")]
|
||||
public class AdminMenuController : Controller
|
||||
{
|
||||
private readonly MongoDBService _mongoService;
|
||||
|
||||
public AdminMenuController(MongoDBService mongoService)
|
||||
{
|
||||
_mongoService = mongoService;
|
||||
}
|
||||
|
||||
[HttpGet("")]
|
||||
public async Task<IActionResult> Index()
|
||||
{
|
||||
var menuItems = await _mongoService.GetAllAsync<MenuItem>("MenuItems");
|
||||
return View(menuItems.OrderBy(m => m.DisplayOrder).ToList());
|
||||
}
|
||||
|
||||
[HttpPost("reseed")]
|
||||
public async Task<IActionResult> ReseedMenu()
|
||||
{
|
||||
// Delete all existing menu items
|
||||
var existingItems = await _mongoService.GetAllAsync<MenuItem>("MenuItems");
|
||||
foreach (var item in existingItems)
|
||||
{
|
||||
await _mongoService.DeleteAsync<MenuItem>("MenuItems", item.Id!);
|
||||
}
|
||||
|
||||
// Add new menu items
|
||||
var defaultMenuItems = new[]
|
||||
{
|
||||
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 }
|
||||
};
|
||||
|
||||
foreach (var item in defaultMenuItems)
|
||||
{
|
||||
await _mongoService.InsertAsync("MenuItems", item);
|
||||
}
|
||||
|
||||
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 (!ModelState.IsValid)
|
||||
{
|
||||
return View(menuItem);
|
||||
}
|
||||
|
||||
menuItem.CreatedAt = DateTime.UtcNow;
|
||||
await _mongoService.InsertAsync("MenuItems", menuItem);
|
||||
TempData["SuccessMessage"] = "Menu item created successfully!";
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
|
||||
[HttpGet("edit/{id}")]
|
||||
public async Task<IActionResult> Edit(string id)
|
||||
{
|
||||
var menuItem = await _mongoService.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 (!ModelState.IsValid)
|
||||
{
|
||||
return View(menuItem);
|
||||
}
|
||||
|
||||
menuItem.Id = id;
|
||||
await _mongoService.UpdateAsync("MenuItems", id, menuItem);
|
||||
TempData["SuccessMessage"] = "Menu item updated successfully!";
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
|
||||
[HttpPost("delete/{id}")]
|
||||
public async Task<IActionResult> Delete(string id)
|
||||
{
|
||||
await _mongoService.DeleteAsync<MenuItem>("MenuItems", id);
|
||||
TempData["SuccessMessage"] = "Menu item deleted successfully!";
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
}
|
||||
}
|
||||
167
Sky_Art_shop/Controllers/AdminPagesController.cs
Normal file
167
Sky_Art_shop/Controllers/AdminPagesController.cs
Normal file
@@ -0,0 +1,167 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using SkyArtShop.Models;
|
||||
using SkyArtShop.Services;
|
||||
|
||||
namespace SkyArtShop.Controllers
|
||||
{
|
||||
[Route("admin/pages")]
|
||||
[Authorize(Roles = "Admin")]
|
||||
public class AdminPagesController : Controller
|
||||
{
|
||||
private readonly MongoDBService _mongoService;
|
||||
private readonly SlugService _slugService;
|
||||
private readonly string _pagesCollection = "Pages";
|
||||
|
||||
public AdminPagesController(MongoDBService mongoService, SlugService slugService)
|
||||
{
|
||||
_mongoService = mongoService;
|
||||
_slugService = slugService;
|
||||
}
|
||||
|
||||
[HttpGet("")]
|
||||
public async Task<IActionResult> Index()
|
||||
{
|
||||
var pages = await _mongoService.GetAllAsync<Page>(_pagesCollection);
|
||||
return View(pages.OrderBy(p => p.PageName).ToList());
|
||||
}
|
||||
|
||||
[HttpGet("create")]
|
||||
public IActionResult Create() => View(new Page());
|
||||
|
||||
[HttpPost("create")]
|
||||
public async Task<IActionResult> Create(Page page)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return View(page);
|
||||
}
|
||||
|
||||
page.CreatedAt = DateTime.UtcNow;
|
||||
page.UpdatedAt = DateTime.UtcNow;
|
||||
page.PageSlug = _slugService.GenerateSlug(page.PageName);
|
||||
await _mongoService.InsertAsync(_pagesCollection, page);
|
||||
TempData["SuccessMessage"] = "Page created successfully!";
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
|
||||
[HttpGet("edit/{id}")]
|
||||
public async Task<IActionResult> Edit(string id)
|
||||
{
|
||||
var page = await _mongoService.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)}");
|
||||
|
||||
// Debug: Check what's in the form
|
||||
foreach (var key in form.Keys)
|
||||
{
|
||||
if (key.StartsWith("ImageGallery") || key.StartsWith("TeamMembers"))
|
||||
{
|
||||
Console.WriteLine($"[ADMIN-PAGES] {key} = {form[key]}");
|
||||
}
|
||||
}
|
||||
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
Console.WriteLine("[ADMIN-PAGES] ModelState is INVALID");
|
||||
foreach (var error in ModelState.Values.SelectMany(v => v.Errors))
|
||||
{
|
||||
Console.WriteLine($"[ADMIN-PAGES] Error: {error.ErrorMessage}");
|
||||
}
|
||||
return View(page);
|
||||
}
|
||||
|
||||
// Get existing page to preserve data
|
||||
var existingPage = await _mongoService.GetByIdAsync<Page>(_pagesCollection, id);
|
||||
if (existingPage == null)
|
||||
{
|
||||
Console.WriteLine($"[ADMIN-PAGES] Page not found: {id}");
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
// Update basic fields
|
||||
existingPage.PageName = page.PageName;
|
||||
existingPage.Title = page.Title;
|
||||
existingPage.Subtitle = page.Subtitle;
|
||||
existingPage.Content = page.Content;
|
||||
existingPage.IsActive = page.IsActive;
|
||||
existingPage.UpdatedAt = DateTime.UtcNow;
|
||||
existingPage.PageSlug = _slugService.GenerateSlug(page.PageName);
|
||||
|
||||
// Manually parse ImageGallery from form
|
||||
existingPage.ImageGallery = new List<string>();
|
||||
foreach (var key in form.Keys.Where(k => k.StartsWith("ImageGallery[")))
|
||||
{
|
||||
var value = form[key].ToString();
|
||||
if (!string.IsNullOrEmpty(value))
|
||||
{
|
||||
existingPage.ImageGallery.Add(value);
|
||||
}
|
||||
}
|
||||
|
||||
// Manually parse TeamMembers from form
|
||||
existingPage.TeamMembers = new List<TeamMember>();
|
||||
var memberIndices = form.Keys
|
||||
.Where(k => k.StartsWith("TeamMembers[") && k.Contains("].Name"))
|
||||
.Select(k =>
|
||||
{
|
||||
var match = System.Text.RegularExpressions.Regex.Match(k, @"TeamMembers\[(\d+)\]");
|
||||
return match.Success ? int.Parse(match.Groups[1].Value) : -1;
|
||||
})
|
||||
.Where(i => i >= 0)
|
||||
.Distinct()
|
||||
.OrderBy(i => i)
|
||||
.ToList();
|
||||
|
||||
foreach (var index in memberIndices)
|
||||
{
|
||||
var member = new TeamMember
|
||||
{
|
||||
Name = form[$"TeamMembers[{index}].Name"].ToString(),
|
||||
Role = form[$"TeamMembers[{index}].Role"].ToString(),
|
||||
Bio = form[$"TeamMembers[{index}].Bio"].ToString(),
|
||||
PhotoUrl = form[$"TeamMembers[{index}].PhotoUrl"].ToString()
|
||||
};
|
||||
existingPage.TeamMembers.Add(member);
|
||||
}
|
||||
|
||||
Console.WriteLine($"[ADMIN-PAGES] Updating page: {existingPage.PageName} (Slug: {existingPage.PageSlug})");
|
||||
Console.WriteLine($"[ADMIN-PAGES] Title: {existingPage.Title}");
|
||||
Console.WriteLine($"[ADMIN-PAGES] Content length: {existingPage.Content?.Length ?? 0}");
|
||||
Console.WriteLine($"[ADMIN-PAGES] Image Gallery Count: {existingPage.ImageGallery.Count}");
|
||||
Console.WriteLine($"[ADMIN-PAGES] Team Members Count: {existingPage.TeamMembers.Count}");
|
||||
|
||||
if (existingPage.ImageGallery.Any())
|
||||
{
|
||||
Console.WriteLine($"[ADMIN-PAGES] Gallery Images: {string.Join(", ", existingPage.ImageGallery)}");
|
||||
}
|
||||
|
||||
if (existingPage.TeamMembers.Any())
|
||||
{
|
||||
foreach (var member in existingPage.TeamMembers)
|
||||
{
|
||||
Console.WriteLine($"[ADMIN-PAGES] Team Member: {member.Name} - {member.Role} - Photo: {member.PhotoUrl}");
|
||||
}
|
||||
}
|
||||
|
||||
await _mongoService.UpdateAsync(_pagesCollection, id, existingPage);
|
||||
TempData["SuccessMessage"] = "Page updated successfully!";
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
|
||||
[HttpPost("delete/{id}")]
|
||||
public async Task<IActionResult> Delete(string id)
|
||||
{
|
||||
await _mongoService.DeleteAsync<Page>(_pagesCollection, id);
|
||||
TempData["SuccessMessage"] = "Page deleted successfully!";
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
}
|
||||
}
|
||||
155
Sky_Art_shop/Controllers/AdminPortfolioController.cs
Normal file
155
Sky_Art_shop/Controllers/AdminPortfolioController.cs
Normal file
@@ -0,0 +1,155 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using SkyArtShop.Models;
|
||||
using SkyArtShop.Services;
|
||||
|
||||
namespace SkyArtShop.Controllers
|
||||
{
|
||||
[Route("admin/portfolio")]
|
||||
[Authorize(Roles="Admin")]
|
||||
public class AdminPortfolioController : Controller
|
||||
{
|
||||
private readonly MongoDBService _mongoService;
|
||||
private readonly SlugService _slugService;
|
||||
private readonly string _categoriesCollection = "PortfolioCategories";
|
||||
private readonly string _projectsCollection = "PortfolioProjects";
|
||||
|
||||
public AdminPortfolioController(MongoDBService mongoService, SlugService slugService)
|
||||
{
|
||||
_mongoService = mongoService;
|
||||
_slugService = slugService;
|
||||
}
|
||||
|
||||
[HttpGet("categories")]
|
||||
public async Task<IActionResult> Categories()
|
||||
{
|
||||
var categories = await _mongoService.GetAllAsync<PortfolioCategory>(_categoriesCollection);
|
||||
return View(categories.OrderBy(c => c.DisplayOrder).ToList());
|
||||
}
|
||||
|
||||
[HttpGet("category/create")]
|
||||
public IActionResult CreateCategory() => View(new PortfolioCategory());
|
||||
|
||||
[HttpPost("category/create")]
|
||||
public async Task<IActionResult> CreateCategory(PortfolioCategory category)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return View(category);
|
||||
}
|
||||
|
||||
category.CreatedAt = DateTime.UtcNow;
|
||||
category.UpdatedAt = DateTime.UtcNow;
|
||||
category.Slug = _slugService.GenerateSlug(category.Name);
|
||||
await _mongoService.InsertAsync(_categoriesCollection, category);
|
||||
TempData["SuccessMessage"] = "Category created successfully!";
|
||||
return RedirectToAction("Categories");
|
||||
}
|
||||
|
||||
[HttpGet("category/edit/{id}")]
|
||||
public async Task<IActionResult> EditCategory(string id)
|
||||
{
|
||||
var category = await _mongoService.GetByIdAsync<PortfolioCategory>(_categoriesCollection, id);
|
||||
if (category == null) return NotFound();
|
||||
return View(category);
|
||||
}
|
||||
|
||||
[HttpPost("category/edit/{id}")]
|
||||
public async Task<IActionResult> EditCategory(string id, PortfolioCategory category)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return View(category);
|
||||
}
|
||||
|
||||
category.Id = id;
|
||||
category.UpdatedAt = DateTime.UtcNow;
|
||||
category.Slug = _slugService.GenerateSlug(category.Name);
|
||||
await _mongoService.UpdateAsync(_categoriesCollection, id, category);
|
||||
TempData["SuccessMessage"] = "Category updated successfully!";
|
||||
return RedirectToAction("Categories");
|
||||
}
|
||||
|
||||
[HttpPost("category/delete/{id}")]
|
||||
public async Task<IActionResult> DeleteCategory(string id)
|
||||
{
|
||||
await _mongoService.DeleteAsync<PortfolioCategory>(_categoriesCollection, id);
|
||||
TempData["SuccessMessage"] = "Category deleted successfully!";
|
||||
return RedirectToAction("Categories");
|
||||
}
|
||||
|
||||
[HttpGet("projects")]
|
||||
public async Task<IActionResult> Projects(string? categoryId)
|
||||
{
|
||||
var projects = await _mongoService.GetAllAsync<PortfolioProject>(_projectsCollection);
|
||||
var categories = await _mongoService.GetAllAsync<PortfolioCategory>(_categoriesCollection);
|
||||
if (!string.IsNullOrEmpty(categoryId))
|
||||
{
|
||||
projects = projects.Where(p => p.CategoryId == categoryId).ToList();
|
||||
}
|
||||
ViewBag.Categories = categories.Where(c => c.IsActive).ToList();
|
||||
ViewBag.SelectedCategory = categoryId;
|
||||
return View(projects.OrderBy(p => p.DisplayOrder).ToList());
|
||||
}
|
||||
|
||||
[HttpGet("project/create")]
|
||||
public async Task<IActionResult> CreateProject()
|
||||
{
|
||||
var categories = await _mongoService.GetAllAsync<PortfolioCategory>(_categoriesCollection);
|
||||
ViewBag.Categories = categories.Where(c => c.IsActive).ToList();
|
||||
return View(new PortfolioProject());
|
||||
}
|
||||
|
||||
[HttpPost("project/create")]
|
||||
public async Task<IActionResult> CreateProject(PortfolioProject project)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
var categories = await _mongoService.GetAllAsync<PortfolioCategory>(_categoriesCollection);
|
||||
ViewBag.Categories = categories.Where(c => c.IsActive).ToList();
|
||||
return View(project);
|
||||
}
|
||||
|
||||
project.CreatedAt = DateTime.UtcNow;
|
||||
project.UpdatedAt = DateTime.UtcNow;
|
||||
await _mongoService.InsertAsync(_projectsCollection, project);
|
||||
TempData["SuccessMessage"] = "Project created successfully!";
|
||||
return RedirectToAction("Projects");
|
||||
}
|
||||
|
||||
[HttpGet("project/edit/{id}")]
|
||||
public async Task<IActionResult> EditProject(string id)
|
||||
{
|
||||
var project = await _mongoService.GetByIdAsync<PortfolioProject>(_projectsCollection, id);
|
||||
if (project == null) return NotFound();
|
||||
var categories = await _mongoService.GetAllAsync<PortfolioCategory>(_categoriesCollection);
|
||||
ViewBag.Categories = categories.Where(c => c.IsActive).ToList();
|
||||
return View(project);
|
||||
}
|
||||
|
||||
[HttpPost("project/edit/{id}")]
|
||||
public async Task<IActionResult> EditProject(string id, PortfolioProject project)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
var categories = await _mongoService.GetAllAsync<PortfolioCategory>(_categoriesCollection);
|
||||
ViewBag.Categories = categories.Where(c => c.IsActive).ToList();
|
||||
return View(project);
|
||||
}
|
||||
|
||||
project.Id = id;
|
||||
project.UpdatedAt = DateTime.UtcNow;
|
||||
await _mongoService.UpdateAsync(_projectsCollection, id, project);
|
||||
TempData["SuccessMessage"] = "Project updated successfully!";
|
||||
return RedirectToAction("Projects");
|
||||
}
|
||||
|
||||
[HttpPost("project/delete/{id}")]
|
||||
public async Task<IActionResult> DeleteProject(string id)
|
||||
{
|
||||
await _mongoService.DeleteAsync<PortfolioProject>(_projectsCollection, id);
|
||||
TempData["SuccessMessage"] = "Project deleted successfully!";
|
||||
return RedirectToAction("Projects");
|
||||
}
|
||||
}
|
||||
}
|
||||
172
Sky_Art_shop/Controllers/AdminProductsController.cs
Normal file
172
Sky_Art_shop/Controllers/AdminProductsController.cs
Normal file
@@ -0,0 +1,172 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using SkyArtShop.Models;
|
||||
using SkyArtShop.Services;
|
||||
|
||||
namespace SkyArtShop.Controllers
|
||||
{
|
||||
[Route("admin/products")]
|
||||
[Authorize(Roles = "Admin")]
|
||||
public class AdminProductsController : Controller
|
||||
{
|
||||
private readonly MongoDBService _mongoService;
|
||||
private readonly SlugService _slugService;
|
||||
private readonly string _productsCollection = "Products";
|
||||
|
||||
public AdminProductsController(MongoDBService mongoService, SlugService slugService)
|
||||
{
|
||||
_mongoService = mongoService;
|
||||
_slugService = slugService;
|
||||
}
|
||||
|
||||
[HttpGet("")]
|
||||
public async Task<IActionResult> Index()
|
||||
{
|
||||
var products = await _mongoService.GetAllAsync<Product>(_productsCollection);
|
||||
return View(products.OrderByDescending(p => p.CreatedAt).ToList());
|
||||
}
|
||||
|
||||
[HttpGet("create")]
|
||||
public IActionResult Create() => View(new Product());
|
||||
|
||||
[HttpPost("create")]
|
||||
public async Task<IActionResult> Create(Product product)
|
||||
{
|
||||
// Remove validation errors for optional fields
|
||||
ModelState.Remove("ShortDescription");
|
||||
ModelState.Remove("Description");
|
||||
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return View(product);
|
||||
}
|
||||
|
||||
// Ensure checkbox defaults when unchecked
|
||||
if (!Request.Form.ContainsKey("IsActive")) product.IsActive = false;
|
||||
if (!Request.Form.ContainsKey("IsFeatured")) product.IsFeatured = false;
|
||||
if (!Request.Form.ContainsKey("IsTopSeller")) product.IsTopSeller = false;
|
||||
|
||||
// Handle multiple colors from form
|
||||
var colors = Request.Form["Colors"].Where(c => !string.IsNullOrEmpty(c)).Select(c => c!).ToList();
|
||||
product.Colors = colors.Any() ? colors : new List<string>();
|
||||
|
||||
// Debug logging
|
||||
Console.WriteLine($"[CREATE] Colors received: {colors.Count}");
|
||||
if (colors.Any())
|
||||
{
|
||||
Console.WriteLine($"[CREATE] Colors: {string.Join(", ", colors)}");
|
||||
}
|
||||
|
||||
product.CreatedAt = DateTime.UtcNow;
|
||||
product.UpdatedAt = DateTime.UtcNow;
|
||||
product.Slug = _slugService.GenerateSlug(product.Name);
|
||||
await _mongoService.InsertAsync(_productsCollection, product);
|
||||
TempData["SuccessMessage"] = "Product created successfully!";
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
|
||||
[HttpGet("edit/{id}")]
|
||||
public async Task<IActionResult> Edit(string id)
|
||||
{
|
||||
var product = await _mongoService.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)
|
||||
{
|
||||
// Remove validation errors for optional fields
|
||||
ModelState.Remove("Images");
|
||||
ModelState.Remove("Slug");
|
||||
ModelState.Remove("ShortDescription");
|
||||
ModelState.Remove("Description");
|
||||
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return View("Create", product);
|
||||
}
|
||||
|
||||
// Ensure checkbox defaults when unchecked
|
||||
if (!Request.Form.ContainsKey("IsActive")) product.IsActive = false;
|
||||
if (!Request.Form.ContainsKey("IsFeatured")) product.IsFeatured = false;
|
||||
if (!Request.Form.ContainsKey("IsTopSeller")) product.IsTopSeller = false;
|
||||
|
||||
// Get existing product to preserve data
|
||||
var existingProduct = await _mongoService.GetByIdAsync<Product>(_productsCollection, id);
|
||||
if (existingProduct == null)
|
||||
{
|
||||
TempData["ErrorMessage"] = "Product not found.";
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
|
||||
// Update editable fields
|
||||
existingProduct.Name = product.Name;
|
||||
existingProduct.ShortDescription = product.ShortDescription;
|
||||
existingProduct.Description = product.Description;
|
||||
existingProduct.Price = product.Price;
|
||||
existingProduct.Category = product.Category;
|
||||
existingProduct.Color = product.Color;
|
||||
|
||||
// Handle multiple colors from form
|
||||
var colors = Request.Form["Colors"].Where(c => !string.IsNullOrEmpty(c)).Select(c => c!).ToList();
|
||||
existingProduct.Colors = colors.Any() ? colors : new List<string>();
|
||||
|
||||
// Debug logging
|
||||
Console.WriteLine($"[EDIT] Colors received: {colors.Count}");
|
||||
if (colors.Any())
|
||||
{
|
||||
Console.WriteLine($"[EDIT] Colors: {string.Join(", ", colors)}");
|
||||
}
|
||||
Console.WriteLine($"[EDIT] Product Colors property: {existingProduct.Colors?.Count ?? 0}");
|
||||
|
||||
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);
|
||||
|
||||
// Update images
|
||||
if (Request.Form.ContainsKey("Images"))
|
||||
{
|
||||
var images = Request.Form["Images"].Where(img => !string.IsNullOrEmpty(img)).Select(img => img!).ToList();
|
||||
existingProduct.Images = images;
|
||||
|
||||
// Set first image as main ImageUrl if not explicitly set
|
||||
if (images.Any() && string.IsNullOrEmpty(product.ImageUrl))
|
||||
{
|
||||
existingProduct.ImageUrl = images[0] ?? "";
|
||||
}
|
||||
}
|
||||
|
||||
// Preserve or update ImageUrl
|
||||
if (!string.IsNullOrEmpty(product.ImageUrl))
|
||||
{
|
||||
existingProduct.ImageUrl = product.ImageUrl;
|
||||
}
|
||||
|
||||
// Update SKU and CostPrice if provided
|
||||
if (!string.IsNullOrEmpty(product.SKU))
|
||||
{
|
||||
existingProduct.SKU = product.SKU;
|
||||
}
|
||||
if (product.CostPrice > 0)
|
||||
{
|
||||
existingProduct.CostPrice = product.CostPrice;
|
||||
}
|
||||
|
||||
await _mongoService.UpdateAsync(_productsCollection, id, existingProduct);
|
||||
TempData["SuccessMessage"] = "Product updated successfully!";
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
|
||||
[HttpPost("delete/{id}")]
|
||||
public async Task<IActionResult> Delete(string id)
|
||||
{
|
||||
await _mongoService.DeleteAsync<Product>(_productsCollection, id);
|
||||
TempData["SuccessMessage"] = "Product deleted successfully!";
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
}
|
||||
}
|
||||
54
Sky_Art_shop/Controllers/AdminSettingsController.cs
Normal file
54
Sky_Art_shop/Controllers/AdminSettingsController.cs
Normal file
@@ -0,0 +1,54 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using SkyArtShop.Models;
|
||||
using SkyArtShop.Services;
|
||||
|
||||
namespace SkyArtShop.Controllers
|
||||
{
|
||||
[Route("admin/settings")]
|
||||
[Authorize(Roles="Admin")]
|
||||
public class AdminSettingsController : Controller
|
||||
{
|
||||
private readonly MongoDBService _mongoService;
|
||||
private readonly string _settingsCollection = "SiteSettings";
|
||||
|
||||
public AdminSettingsController(MongoDBService mongoService)
|
||||
{
|
||||
_mongoService = mongoService;
|
||||
}
|
||||
|
||||
[HttpGet("")]
|
||||
public async Task<IActionResult> Index()
|
||||
{
|
||||
var settingsList = await _mongoService.GetAllAsync<SiteSettings>(_settingsCollection);
|
||||
var settings = settingsList.FirstOrDefault();
|
||||
if (settings == null)
|
||||
{
|
||||
settings = new SiteSettings();
|
||||
await _mongoService.InsertAsync(_settingsCollection, settings);
|
||||
}
|
||||
return View(settings);
|
||||
}
|
||||
|
||||
[HttpPost("update")]
|
||||
public async Task<IActionResult> Update(SiteSettings settings)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return View("Index", settings);
|
||||
}
|
||||
|
||||
settings.UpdatedAt = DateTime.UtcNow;
|
||||
if (!string.IsNullOrEmpty(settings.Id))
|
||||
{
|
||||
await _mongoService.UpdateAsync(_settingsCollection, settings.Id, settings);
|
||||
}
|
||||
else
|
||||
{
|
||||
await _mongoService.InsertAsync(_settingsCollection, settings);
|
||||
}
|
||||
TempData["SuccessMessage"] = "Site settings updated successfully!";
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
}
|
||||
}
|
||||
106
Sky_Art_shop/Controllers/AdminUploadController.cs
Normal file
106
Sky_Art_shop/Controllers/AdminUploadController.cs
Normal file
@@ -0,0 +1,106 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using SkyArtShop.Services;
|
||||
|
||||
namespace SkyArtShop.Controllers
|
||||
{
|
||||
[Route("admin/upload")]
|
||||
[Authorize(Roles="Admin")]
|
||||
public class AdminUploadController : Controller
|
||||
{
|
||||
private readonly IWebHostEnvironment _environment;
|
||||
|
||||
public AdminUploadController(IWebHostEnvironment environment)
|
||||
{
|
||||
_environment = environment;
|
||||
}
|
||||
|
||||
[HttpGet("")]
|
||||
public IActionResult Index()
|
||||
{
|
||||
var uploadsPath = Path.Combine(_environment.WebRootPath, "uploads", "images");
|
||||
var images = new List<string>();
|
||||
|
||||
if (Directory.Exists(uploadsPath))
|
||||
{
|
||||
var files = Directory.GetFiles(uploadsPath)
|
||||
.Select(f => $"/uploads/images/{Path.GetFileName(f)}")
|
||||
.OrderByDescending(f => f)
|
||||
.ToList();
|
||||
images = files;
|
||||
}
|
||||
|
||||
return View(images);
|
||||
}
|
||||
|
||||
[HttpPost("image")]
|
||||
public async Task<IActionResult> UploadImage(IFormFile file)
|
||||
{
|
||||
if (file == null || file.Length == 0)
|
||||
{
|
||||
return Json(new { success = false, message = "No file uploaded" });
|
||||
}
|
||||
var allowedExtensions = new[] { ".jpg", ".jpeg", ".png", ".gif", ".webp" };
|
||||
var extension = Path.GetExtension(file.FileName).ToLowerInvariant();
|
||||
if (!allowedExtensions.Contains(extension))
|
||||
{
|
||||
return Json(new { success = false, message = "Invalid file type" });
|
||||
}
|
||||
try
|
||||
{
|
||||
var uploadsPath = Path.Combine(_environment.WebRootPath, "uploads", "images");
|
||||
if (!Directory.Exists(uploadsPath)) Directory.CreateDirectory(uploadsPath);
|
||||
var fileName = $"{Guid.NewGuid()}{extension}";
|
||||
var filePath = Path.Combine(uploadsPath, fileName);
|
||||
using var stream = new FileStream(filePath, 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)
|
||||
{
|
||||
var uploadedUrls = new List<string>();
|
||||
foreach (var file in files)
|
||||
{
|
||||
if (file == null || file.Length == 0) continue;
|
||||
var extension = Path.GetExtension(file.FileName).ToLowerInvariant();
|
||||
var allowedExtensions = new[] { ".jpg", ".jpeg", ".png", ".gif", ".webp" };
|
||||
if (!allowedExtensions.Contains(extension)) continue;
|
||||
var uploadsPath = Path.Combine(_environment.WebRootPath, "uploads", "images");
|
||||
if (!Directory.Exists(uploadsPath)) Directory.CreateDirectory(uploadsPath);
|
||||
var fileName = $"{Guid.NewGuid()}{extension}";
|
||||
var filePath = Path.Combine(uploadsPath, fileName);
|
||||
using var stream = new FileStream(filePath, 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
|
||||
{
|
||||
var fileName = Path.GetFileName(imageUrl);
|
||||
var filePath = Path.Combine(_environment.WebRootPath, "uploads", "images", fileName);
|
||||
if (System.IO.File.Exists(filePath))
|
||||
{
|
||||
System.IO.File.Delete(filePath);
|
||||
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 });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
62
Sky_Art_shop/Controllers/ApiUploadController.cs
Normal file
62
Sky_Art_shop/Controllers/ApiUploadController.cs
Normal file
@@ -0,0 +1,62 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
|
||||
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 == 0)
|
||||
{
|
||||
return Json(new { success = false, message = "No file uploaded" });
|
||||
}
|
||||
|
||||
var allowedExtensions = new[] { ".jpg", ".jpeg", ".png", ".gif", ".webp" };
|
||||
var extension = Path.GetExtension(image.FileName).ToLowerInvariant();
|
||||
|
||||
if (!allowedExtensions.Contains(extension))
|
||||
{
|
||||
return Json(new { success = false, message = "Invalid file type. Only images are allowed." });
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var uploadsPath = Path.Combine(_environment.WebRootPath, "uploads", "images");
|
||||
if (!Directory.Exists(uploadsPath))
|
||||
{
|
||||
Directory.CreateDirectory(uploadsPath);
|
||||
}
|
||||
|
||||
var fileName = $"{Guid.NewGuid()}{extension}";
|
||||
var filePath = Path.Combine(uploadsPath, fileName);
|
||||
|
||||
using (var stream = new FileStream(filePath, FileMode.Create))
|
||||
{
|
||||
await image.CopyToAsync(stream);
|
||||
}
|
||||
|
||||
var imageUrl = $"/uploads/images/{fileName}";
|
||||
|
||||
Console.WriteLine($"[API-UPLOAD] Image uploaded successfully: {imageUrl}");
|
||||
|
||||
return Json(new { success = true, imageUrl = imageUrl });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"[API-UPLOAD] Upload failed: {ex.Message}");
|
||||
return Json(new { success = false, message = $"Upload failed: {ex.Message}" });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
41
Sky_Art_shop/Controllers/BlogController.cs
Normal file
41
Sky_Art_shop/Controllers/BlogController.cs
Normal file
@@ -0,0 +1,41 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using SkyArtShop.Models;
|
||||
using SkyArtShop.Services;
|
||||
|
||||
namespace SkyArtShop.Controllers
|
||||
{
|
||||
public class BlogController : Controller
|
||||
{
|
||||
private readonly MongoDBService _mongoService;
|
||||
private readonly string _blogCollection = "BlogPosts";
|
||||
|
||||
public BlogController(MongoDBService mongoService)
|
||||
{
|
||||
_mongoService = mongoService;
|
||||
}
|
||||
|
||||
public async Task<IActionResult> Index()
|
||||
{
|
||||
var posts = await _mongoService.GetAllAsync<BlogPost>(_blogCollection);
|
||||
var publishedPosts = posts
|
||||
.Where(p => p.IsPublished)
|
||||
.OrderByDescending(p => p.PublishedDate)
|
||||
.ToList();
|
||||
|
||||
return View(publishedPosts);
|
||||
}
|
||||
|
||||
public async Task<IActionResult> Post(string slug)
|
||||
{
|
||||
var posts = await _mongoService.GetAllAsync<BlogPost>(_blogCollection);
|
||||
var post = posts.FirstOrDefault(p => p.Slug == slug && p.IsPublished);
|
||||
|
||||
if (post == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
return View(post);
|
||||
}
|
||||
}
|
||||
}
|
||||
34
Sky_Art_shop/Controllers/ContactController.cs
Normal file
34
Sky_Art_shop/Controllers/ContactController.cs
Normal file
@@ -0,0 +1,34 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using SkyArtShop.Models;
|
||||
using SkyArtShop.Services;
|
||||
|
||||
namespace SkyArtShop.Controllers
|
||||
{
|
||||
public class ContactController : Controller
|
||||
{
|
||||
private readonly MongoDBService _mongoService;
|
||||
private readonly string _settingsCollection = "SiteSettings";
|
||||
|
||||
public ContactController(MongoDBService mongoService)
|
||||
{
|
||||
_mongoService = mongoService;
|
||||
}
|
||||
|
||||
public async Task<IActionResult> Index()
|
||||
{
|
||||
var settingsList = await _mongoService.GetAllAsync<SiteSettings>(_settingsCollection);
|
||||
var settings = settingsList.FirstOrDefault() ?? new SiteSettings();
|
||||
|
||||
return View(settings);
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public IActionResult Submit(string name, string email, string phone, string subject, string message)
|
||||
{
|
||||
// Here you would implement email sending logic
|
||||
// For now, just return a success message
|
||||
TempData["Success"] = "Thank you! Your message has been sent. We'll get back to you soon.";
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
}
|
||||
}
|
||||
36
Sky_Art_shop/Controllers/DiagnosticsController.cs
Normal file
36
Sky_Art_shop/Controllers/DiagnosticsController.cs
Normal file
@@ -0,0 +1,36 @@
|
||||
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 products = await _mongoService.GetAllAsync<Product>("Products");
|
||||
|
||||
var report = products.Select(p => new
|
||||
{
|
||||
p.Id,
|
||||
p.Name,
|
||||
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(report);
|
||||
}
|
||||
}
|
||||
}
|
||||
64
Sky_Art_shop/Controllers/HomeController.cs
Normal file
64
Sky_Art_shop/Controllers/HomeController.cs
Normal file
@@ -0,0 +1,64 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using SkyArtShop.Models;
|
||||
using SkyArtShop.Services;
|
||||
|
||||
namespace SkyArtShop.Controllers
|
||||
{
|
||||
public class HomeController : Controller
|
||||
{
|
||||
private readonly MongoDBService _mongoService;
|
||||
private readonly string _settingsCollection = "SiteSettings";
|
||||
private readonly string _productsCollection = "Products";
|
||||
private readonly string _sectionsCollection = "HomepageSections";
|
||||
|
||||
public HomeController(MongoDBService mongoService)
|
||||
{
|
||||
_mongoService = mongoService;
|
||||
}
|
||||
|
||||
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
|
||||
public async Task<IActionResult> Index()
|
||||
{
|
||||
var settings = await GetSiteSettings();
|
||||
var topProducts = await GetTopSellerProducts();
|
||||
var sections = await GetHomepageSections();
|
||||
|
||||
ViewBag.Settings = settings;
|
||||
ViewBag.TopProducts = topProducts;
|
||||
ViewBag.Sections = sections;
|
||||
|
||||
return View();
|
||||
}
|
||||
|
||||
private async Task<SiteSettings> GetSiteSettings()
|
||||
{
|
||||
var settingsList = await _mongoService.GetAllAsync<SiteSettings>(_settingsCollection);
|
||||
return settingsList.FirstOrDefault() ?? new SiteSettings();
|
||||
}
|
||||
|
||||
private async Task<List<Product>> GetTopSellerProducts()
|
||||
{
|
||||
var products = await _mongoService.GetAllAsync<Product>(_productsCollection);
|
||||
return products.Where(p => p.IsTopSeller && p.IsActive).Take(4).ToList();
|
||||
}
|
||||
|
||||
private async Task<List<HomepageSection>> GetHomepageSections()
|
||||
{
|
||||
var sections = await _mongoService.GetAllAsync<HomepageSection>(_sectionsCollection);
|
||||
Console.WriteLine($"Total sections from DB: {sections.Count}");
|
||||
var activeSections = sections.Where(s => s.IsActive).OrderBy(s => s.DisplayOrder).ToList();
|
||||
Console.WriteLine($"Active sections: {activeSections.Count}");
|
||||
foreach (var section in activeSections)
|
||||
{
|
||||
Console.WriteLine($"Section: {section.Title} | Type: {section.SectionType} | Order: {section.DisplayOrder} | Active: {section.IsActive}");
|
||||
}
|
||||
return activeSections;
|
||||
}
|
||||
|
||||
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
|
||||
public IActionResult Error()
|
||||
{
|
||||
return View();
|
||||
}
|
||||
}
|
||||
}
|
||||
31
Sky_Art_shop/Controllers/PageController.cs
Normal file
31
Sky_Art_shop/Controllers/PageController.cs
Normal file
@@ -0,0 +1,31 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using SkyArtShop.Models;
|
||||
using SkyArtShop.Services;
|
||||
|
||||
namespace SkyArtShop.Controllers
|
||||
{
|
||||
[Route("page")]
|
||||
public class PageController : Controller
|
||||
{
|
||||
private readonly MongoDBService _mongoService;
|
||||
|
||||
public PageController(MongoDBService mongoService)
|
||||
{
|
||||
_mongoService = mongoService;
|
||||
}
|
||||
|
||||
[HttpGet("{slug}")]
|
||||
public async Task<IActionResult> Index(string slug)
|
||||
{
|
||||
var pages = await _mongoService.GetAllAsync<Page>("Pages");
|
||||
var page = pages.FirstOrDefault(p => p.PageSlug == slug && p.IsActive);
|
||||
|
||||
if (page == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
return View("View", page);
|
||||
}
|
||||
}
|
||||
}
|
||||
58
Sky_Art_shop/Controllers/PortfolioController.cs
Normal file
58
Sky_Art_shop/Controllers/PortfolioController.cs
Normal file
@@ -0,0 +1,58 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using SkyArtShop.Models;
|
||||
using SkyArtShop.Services;
|
||||
|
||||
namespace SkyArtShop.Controllers
|
||||
{
|
||||
public class PortfolioController : Controller
|
||||
{
|
||||
private readonly MongoDBService _mongoService;
|
||||
private readonly string _categoriesCollection = "PortfolioCategories";
|
||||
private readonly string _projectsCollection = "PortfolioProjects";
|
||||
|
||||
public PortfolioController(MongoDBService mongoService)
|
||||
{
|
||||
_mongoService = mongoService;
|
||||
}
|
||||
|
||||
public async Task<IActionResult> Index()
|
||||
{
|
||||
var categories = await _mongoService.GetAllAsync<PortfolioCategory>(_categoriesCollection);
|
||||
var activeCategories = categories.Where(c => c.IsActive).OrderBy(c => c.DisplayOrder).ToList();
|
||||
|
||||
return View(activeCategories);
|
||||
}
|
||||
|
||||
public async Task<IActionResult> Category(string slug)
|
||||
{
|
||||
var categories = await _mongoService.GetAllAsync<PortfolioCategory>(_categoriesCollection);
|
||||
var category = categories.FirstOrDefault(c => c.Slug == slug && c.IsActive);
|
||||
|
||||
if (category == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
var projects = await _mongoService.GetAllAsync<PortfolioProject>(_projectsCollection);
|
||||
var categoryProjects = projects
|
||||
.Where(p => p.CategoryId == category.Id && p.IsActive)
|
||||
.OrderBy(p => p.DisplayOrder)
|
||||
.ToList();
|
||||
|
||||
ViewBag.Category = category;
|
||||
return View(categoryProjects);
|
||||
}
|
||||
|
||||
public async Task<IActionResult> Project(string id)
|
||||
{
|
||||
var project = await _mongoService.GetByIdAsync<PortfolioProject>(_projectsCollection, id);
|
||||
|
||||
if (project == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
return View(project);
|
||||
}
|
||||
}
|
||||
}
|
||||
102
Sky_Art_shop/Controllers/ShopController.cs
Normal file
102
Sky_Art_shop/Controllers/ShopController.cs
Normal file
@@ -0,0 +1,102 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using SkyArtShop.Models;
|
||||
using SkyArtShop.Services;
|
||||
|
||||
namespace SkyArtShop.Controllers
|
||||
{
|
||||
public class ShopController : Controller
|
||||
{
|
||||
private readonly MongoDBService _mongoService;
|
||||
private readonly string _productsCollection = "Products";
|
||||
|
||||
public ShopController(MongoDBService mongoService)
|
||||
{
|
||||
_mongoService = mongoService;
|
||||
}
|
||||
|
||||
public async Task<IActionResult> Index(string? category, string? sort)
|
||||
{
|
||||
var products = await _mongoService.GetAllAsync<Product>(_productsCollection);
|
||||
var activeProducts = products.Where(p => p.IsActive).ToList();
|
||||
|
||||
// Filter by category if specified
|
||||
if (!string.IsNullOrEmpty(category) && category != "all")
|
||||
{
|
||||
activeProducts = activeProducts.Where(p => p.Category == category).ToList();
|
||||
}
|
||||
|
||||
// Sort products
|
||||
activeProducts = sort switch
|
||||
{
|
||||
"price-low" => activeProducts.OrderBy(p => p.Price).ToList(),
|
||||
"price-high" => activeProducts.OrderByDescending(p => p.Price).ToList(),
|
||||
"newest" => activeProducts.OrderByDescending(p => p.CreatedAt).ToList(),
|
||||
_ => activeProducts.OrderByDescending(p => p.IsFeatured).ToList()
|
||||
};
|
||||
|
||||
ViewBag.SelectedCategory = category ?? "all";
|
||||
ViewBag.SelectedSort = sort ?? "featured";
|
||||
ViewBag.Categories = activeProducts.Select(p => p.Category).Distinct().ToList();
|
||||
|
||||
return View(activeProducts);
|
||||
}
|
||||
|
||||
public async Task<IActionResult> Product(string id)
|
||||
{
|
||||
var product = await _mongoService.GetByIdAsync<Product>(_productsCollection, id);
|
||||
|
||||
if (product == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
// Debug logging
|
||||
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"}");
|
||||
|
||||
// Track product view
|
||||
var sessionId = HttpContext.Session.Id;
|
||||
var ipAddress = HttpContext.Connection.RemoteIpAddress?.ToString() ?? "unknown";
|
||||
var productView = new ProductView
|
||||
{
|
||||
ProductId = id,
|
||||
SessionId = sessionId,
|
||||
IpAddress = ipAddress,
|
||||
ViewedAt = DateTime.UtcNow
|
||||
};
|
||||
await _mongoService.InsertAsync("ProductViews", productView);
|
||||
|
||||
// Get related products based on:
|
||||
// 1. Same category
|
||||
// 2. Most viewed products
|
||||
// 3. Exclude current product
|
||||
var allProducts = await _mongoService.GetAllAsync<Product>(_productsCollection);
|
||||
var allViews = await _mongoService.GetAllAsync<ProductView>("ProductViews");
|
||||
|
||||
// Count views for each product
|
||||
var productViewCounts = allViews
|
||||
.GroupBy(v => v.ProductId)
|
||||
.ToDictionary(g => g.Key, g => g.Count());
|
||||
|
||||
var relatedProducts = allProducts
|
||||
.Where(p => p.IsActive && p.Id != id)
|
||||
.OrderByDescending(p =>
|
||||
(p.Category == product.Category ? 100 : 0) + // Same category bonus
|
||||
(productViewCounts.ContainsKey(p.Id ?? "") ? productViewCounts[p.Id ?? ""] : 0) + // View count
|
||||
(p.IsFeatured ? 50 : 0) + // Featured bonus
|
||||
(p.UnitsSold * 2) // Sales popularity
|
||||
)
|
||||
.Take(4)
|
||||
.ToList();
|
||||
|
||||
ViewBag.RelatedProducts = relatedProducts;
|
||||
return View(product);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user