Files
SkyArtShop/website/public/shop.html
Local Server f818fff3a5 Style: Unify cart Continue Shopping button with wishlist style
Updated cart dropdown Continue Shopping buttons to use btn-outline
class (matching wishlist style) instead of btn-text across all pages.

Changes:
- shop.html: btn-text → btn-outline
- contact.html: btn-text → btn-outline
- product.html: btn-text → btn-outline
- about.html: btn-text → btn-outline

All cart Continue Shopping buttons now have consistent styling
with the wishlist Continue Shopping button (purple outline style).
2026-01-14 20:47:32 -06:00

1261 lines
37 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="description" content="Shop all products - Sky Art Shop" />
<title>Shop - Sky Art Shop</title>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;600;700&family=Inter:wght@400;500;600;700&family=Poppins:wght@600;700;800&display=swap"
rel="stylesheet"
/>
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css"
/>
<link rel="stylesheet" href="/assets/css/theme-colors.css" />
<link rel="stylesheet" href="/assets/css/main.css?v=1735692100" />
<link rel="stylesheet" href="/assets/css/navbar.css?v=1767233028" />
<link rel="stylesheet" href="/assets/css/page-overrides.css?v=1736790001" />
<link rel="stylesheet" href="/assets/css/cart-wishlist.css" />
<link rel="stylesheet" href="/assets/css/shopping.css" />
<link rel="stylesheet" href="/assets/css/responsive.css" />
<link
rel="stylesheet"
href="/assets/css/navbar-mobile-fix.css?v=1736790000"
/>
<style>
/* Body Reset */
body {
margin: 0;
padding: 0;
background-color: #ffebeb;
min-height: 100vh;
}
/* Sticky Banner Container */
.sticky-banner-wrapper {
position: sticky;
top: 0;
z-index: 1000;
background: #ffd0d0;
}
/* Override navbar position when inside sticky wrapper */
.sticky-banner-wrapper .modern-navbar {
position: relative;
box-shadow: none;
}
/* Shipping Banner */
.shipping-banner {
background: linear-gradient(135deg, #f6ccde 0%, #fcb1d8 100%);
color: #202023;
text-align: center;
padding: 12px 0;
font-size: 14px;
font-weight: 500;
}
/* Utility Bar - Combined Header and Search */
.utility-bar {
background: #ffd0d0;
padding: 24px 0;
border-bottom: 1px solid #fcb1d8;
}
.utility-bar-content {
display: flex;
justify-content: space-between;
align-items: center;
gap: 40px;
}
.utility-bar-left {
flex: 1;
max-width: 500px;
}
.utility-bar-left h1 {
font-family: "Roboto", sans-serif;
font-size: 28px;
font-weight: 700;
color: #202023;
margin: 0 0 8px 0;
line-height: 1.2;
}
.utility-bar-left p {
font-family: "Roboto", sans-serif;
font-size: 15px;
font-weight: 400;
color: #202023;
margin: 0;
line-height: 1.5;
}
.utility-bar-right {
flex: 0 0 450px;
}
.search-container {
position: relative;
width: 100%;
}
.search-container input {
width: 100%;
padding: 14px 60px 14px 20px;
border: 2px solid #ffd0d0;
border-radius: 50px;
font-family: "Roboto", sans-serif;
font-size: 15px;
transition: all 0.3s;
background: white;
color: #202023;
}
.search-container input:focus {
outline: none;
border-color: #fcb1d8;
box-shadow: 0 0 0 3px rgba(252, 177, 216, 0.2);
}
.search-container button {
position: absolute;
right: 5px;
top: 50%;
transform: translateY(-50%);
background: #fcb1d8;
color: #202023;
border: none;
padding: 10px 24px;
border-radius: 50px;
cursor: pointer;
font-family: "Roboto", sans-serif;
font-weight: 600;
font-size: 14px;
transition: background 0.3s;
}
.search-container button:hover {
background: #f6ccde;
}
/* Shop Page Container Override */
body .container {
max-width: 1400px;
padding: 0 40px;
}
/* Shop Page Container Override - Extend to more screen width */
body .container {
max-width: 1400px;
padding: 0 40px;
}
/* Category Chips */
.categories-section {
padding: 20px 0;
background: #ffebeb;
border-bottom: 1px solid #ffd0d0;
}
.category-chips {
display: flex;
gap: 12px;
flex-wrap: wrap;
}
.category-chip {
padding: 10px 24px;
background: white;
border: 2px solid #ffd0d0;
border-radius: 50px;
cursor: pointer;
font-weight: 500;
color: #202023;
transition: all 0.3s;
font-size: 14px;
}
.category-chip:hover {
border-color: #fcb1d8;
color: #fcb1d8;
transform: translateY(-2px);
}
.category-chip.active {
background: #fcb1d8;
border-color: #fcb1d8;
color: #202023;
}
/* Products Grid */
.shop-main {
min-height: 400px;
}
.shop-toolbar {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 30px;
flex-wrap: wrap;
gap: 16px;
}
.results-count {
font-family: "Roboto", sans-serif;
font-size: 16px;
color: #2d3436;
font-weight: 500;
}
.filter-select {
padding: 10px 16px;
border: 1px solid #e1e8ed;
border-radius: 8px;
font-family: "Roboto", sans-serif;
font-size: 14px;
background: white;
cursor: pointer;
transition: all 0.3s;
}
.filter-select:focus {
outline: none;
border-color: #ff6b6b;
}
.products-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
gap: 20px;
margin-bottom: 40px;
}
body .product-card {
background: white;
border: 1px solid #ffd0d0;
border-radius: 12px;
overflow: hidden !important;
transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
position: relative;
display: flex !important;
flex-direction: column !important;
box-shadow: 0 2px 8px rgba(252, 177, 216, 0.15);
}
.product-card:hover {
transform: translateY(-8px);
box-shadow: 0 8px 20px rgba(252, 177, 216, 0.3);
border-color: #fcb1d8;
}
.product-badges {
position: absolute;
top: 12px;
left: 12px;
z-index: 10;
display: flex;
flex-direction: column;
gap: 6px;
}
.badge-featured {
display: inline-flex;
align-items: center;
gap: 3px;
padding: 5px 10px;
background: #fcb1d8;
color: #202023;
border-radius: 6px;
font-size: 10px;
font-weight: 600;
box-shadow: 0 2px 4px rgba(252, 177, 216, 0.4);
}
.badge-bestseller {
display: inline-flex;
align-items: center;
gap: 3px;
padding: 5px 10px;
background: #f6ccde;
color: #202023;
border-radius: 6px;
font-size: 10px;
font-weight: 600;
box-shadow: 0 2px 4px rgba(252, 177, 216, 0.4);
}
.product-link {
display: block;
text-decoration: none;
color: inherit;
flex-shrink: 0;
}
.product-image {
width: 100%;
height: 220px;
overflow: hidden;
background: #f8f9fa;
flex-shrink: 0;
position: relative;
}
.product-image img {
width: 100%;
height: 100%;
object-fit: cover;
transition: transform 0.3s;
}
.product-card:hover .product-image img {
transform: scale(1.05);
}
body .product-card h3 {
font-family: "Roboto", sans-serif;
font-size: 15px;
font-weight: 600;
color: #1a1a1a !important;
margin: 0 0 6px 0;
line-height: 1.4;
display: block !important;
visibility: visible !important;
opacity: 1 !important;
height: auto !important;
overflow: visible !important;
position: static !important;
}
/* Product Title Link - Make entire title clickable */
.product-title-link {
text-decoration: none !important;
color: #202023 !important;
display: block !important;
cursor: pointer !important;
transition: color 0.3s ease;
position: relative;
z-index: 10;
}
.product-title-link:hover {
color: #fcb1d8 !important;
}
.product-title-link h3 {
color: inherit;
transition: color 0.3s ease;
margin: 0 0 8px 0;
pointer-events: none;
}
.product-title-link:hover h3 {
color: #fcb1d8 !important;
}
body .product-info {
padding: 16px;
background: white;
display: block !important;
visibility: visible !important;
opacity: 1 !important;
height: auto !important;
overflow: visible !important;
min-height: 120px;
position: relative !important;
transform: none !important;
transition: none !important;
flex-grow: 1;
flex-shrink: 0;
}
.product-actions {
display: flex;
gap: 0.5rem;
padding: 0 14px 14px;
flex-shrink: 0;
}
body .product-description {
font-family: "Roboto", sans-serif;
font-size: 13px;
color: #6b7280 !important;
margin: 0 0 8px 0;
line-height: 1.5;
display: block !important;
visibility: visible !important;
opacity: 1 !important;
height: auto !important;
position: static !important;
}
.product-category {
font-family: "Roboto", sans-serif;
font-size: 12px;
color: #6b7280 !important;
margin: 0 0 10px 0;
display: flex !important;
align-items: center;
gap: 6px;
visibility: visible !important;
opacity: 1 !important;
height: auto !important;
}
.product-category i {
font-size: 14px;
}
body .product-footer {
display: flex !important;
justify-content: space-between;
align-items: center;
visibility: visible !important;
opacity: 1 !important;
height: auto !important;
position: static !important;
}
body .product-card .price {
font-family: "Roboto", sans-serif;
font-size: 18px;
font-weight: 700;
color: #6b46c1 !important;
margin: 0;
display: block !important;
visibility: visible !important;
opacity: 1 !important;
height: auto !important;
position: static !important;
}
.stock-status {
font-family: "Roboto", sans-serif;
font-size: 12px;
font-weight: 500;
margin: 0;
display: flex !important;
align-items: center;
gap: 4px;
visibility: visible !important;
}
.stock-status.in-stock {
color: #10b981 !important;
}
.stock-status.out-of-stock {
color: #ef4444 !important;
}
.stock-status i {
font-size: 14px;
}
.product-color-badge {
display: inline-block;
padding: 4px 12px;
background: #f8f9fa;
border-radius: 12px;
font-size: 12px;
color: #636e72;
margin: 0 16px 12px;
}
.btn {
display: inline-flex;
align-items: center;
justify-content: center;
padding: 8px 16px;
border: none;
border-radius: 8px;
font-family: "Roboto", sans-serif;
font-size: 14px;
font-weight: 500;
cursor: pointer;
transition: all 0.3s;
}
.btn-small {
padding: 8px 12px;
}
.btn-icon {
background: #f8f9fa;
color: #636e72;
border: 1px solid #e1e8ed;
}
.btn-icon:hover {
background: #ff6b6b;
color: white;
border-color: #ff6b6b;
}
#noProducts {
text-align: center;
padding: 60px 20px;
font-family: "Roboto", sans-serif;
font-size: 16px;
color: #636e72;
}
/* Notification Animations */
@keyframes slideIn {
from {
transform: translateX(400px);
opacity: 0;
}
to {
transform: translateX(0);
opacity: 1;
}
}
@keyframes slideOut {
from {
transform: translateX(0);
opacity: 1;
}
to {
transform: translateX(400px);
opacity: 0;
}
}
@media (max-width: 1200px) {
body .container {
max-width: 100%;
padding: 0 30px;
}
.products-grid {
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: 18px;
}
}
@media (max-width: 968px) {
body .container {
padding: 0 20px;
}
.products-grid {
grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
gap: 14px;
}
.product-image {
height: 180px;
}
.utility-bar-content {
flex-direction: column;
gap: 20px;
}
.utility-bar-left {
max-width: 100%;
text-align: center;
}
.utility-bar-left h1 {
font-size: 24px;
}
.utility-bar-left p {
font-size: 14px;
}
.utility-bar-right {
flex: 1;
width: 100%;
}
.category-chips {
overflow-x: auto;
flex-wrap: nowrap;
padding-bottom: 10px;
}
.category-chip {
white-space: nowrap;
}
}
</style>
<link rel="stylesheet" href="/assets/css/theme-colors.css" />
</head>
<body>
<!-- Sticky Banner Wrapper -->
<div class="sticky-banner-wrapper">
<!-- Modern Navigation -->
<nav class="modern-navbar">
<div class="navbar-wrapper">
<div class="navbar-brand">
<a href="/home" class="brand-link">
<img
src="/uploads/cat-png-1767324141436-368259437.png"
alt="Sky Art Shop Logo"
class="brand-logo"
/>
<span class="brand-name">Sky' Art Shop</span>
</a>
</div>
<div class="navbar-menu">
<ul class="nav-menu-list">
<li class="nav-item">
<a href="/home" class="nav-link">Home</a>
</li>
<li class="nav-item">
<a href="/shop" class="nav-link active">Shop</a>
</li>
<li class="nav-item">
<a href="/portfolio" class="nav-link">Portfolio</a>
</li>
<li class="nav-item">
<a href="/about" class="nav-link">About</a>
</li>
<li class="nav-item">
<a href="/blog" class="nav-link">Blog</a>
</li>
<li class="nav-item">
<a href="/contact" class="nav-link">Contact</a>
</li>
</ul>
</div>
<div class="navbar-actions">
<div class="action-item wishlist-dropdown-wrapper">
<button
class="action-btn"
id="wishlistToggle"
aria-label="Wishlist"
>
<i class="bi bi-heart"></i>
<span class="action-badge" id="wishlistCount">0</span>
</button>
<div class="action-dropdown wishlist-dropdown" id="wishlistPanel">
<div class="dropdown-head">
<h3>My Wishlist</h3>
<button class="dropdown-close" id="wishlistClose">
<i class="bi bi-x-lg"></i>
</button>
</div>
<div class="dropdown-body" id="wishlistContent">
<p class="empty-state">Your wishlist is empty</p>
</div>
<div class="dropdown-foot">
<a href="/shop" class="btn-outline">Continue Shopping</a>
</div>
</div>
</div>
<div class="action-item cart-dropdown-wrapper">
<button
class="action-btn"
id="cartToggle"
aria-label="Shopping Cart"
>
<i class="bi bi-cart3"></i>
<span class="action-badge" id="cartCount">0</span>
</button>
<div class="action-dropdown cart-dropdown" id="cartPanel">
<div class="dropdown-head">
<h3>Shopping Cart</h3>
<button class="dropdown-close" id="cartClose">
<i class="bi bi-x-lg"></i>
</button>
</div>
<div class="dropdown-body" id="cartContent">
<p class="empty-state">Your cart is empty</p>
</div>
<div class="dropdown-foot">
<div class="cart-summary">
<span class="summary-label">Subtotal:</span>
<span class="summary-value" id="cartSubtotal">$0.00</span>
</div>
<a href="/checkout" class="btn-primary-full"
>Proceed to Checkout</a
>
<a href="/shop" class="btn-outline">Continue Shopping</a>
</div>
</div>
</div>
<button
class="mobile-toggle"
id="mobileMenuToggle"
aria-label="Menu"
>
<span class="toggle-line"></span>
<span class="toggle-line"></span>
<span class="toggle-line"></span>
</button>
</div>
</div>
<div class="mobile-menu" id="mobileMenu">
<div class="mobile-menu-header">
<span class="mobile-brand">Sky' Art Shop</span>
<button class="mobile-close" id="mobileMenuClose">
<i class="bi bi-x-lg"></i>
</button>
</div>
<ul class="mobile-menu-list">
<li><a href="/home" class="mobile-link">Home</a></li>
<li><a href="/shop" class="mobile-link">Shop</a></li>
<li><a href="/portfolio" class="mobile-link">Portfolio</a></li>
<li><a href="/about" class="mobile-link">About</a></li>
<li><a href="/blog" class="mobile-link">Blog</a></li>
<li><a href="/contact" class="mobile-link">Contact</a></li>
</ul>
</div>
</nav>
<!-- Shipping Banner -->
<div class="shipping-banner">⚡ Free shipping on orders over $50</div>
</div>
<!-- Utility Bar: Header + Search -->
<section class="utility-bar">
<div class="container">
<div class="utility-bar-content">
<!-- Left: Title and Description -->
<div class="utility-bar-left">
<h1>Shop All Products</h1>
<p>
Discover unique art pieces and creative supplies for your next
project
</p>
</div>
<!-- Right: Search Bar -->
<div class="utility-bar-right">
<div class="search-container">
<input
type="search"
placeholder="Search products..."
id="productSearch"
/>
<button type="button">Search</button>
</div>
</div>
</div>
</div>
</section>
<!-- Categories -->
<section class="categories-section">
<div class="container">
<div class="category-chips">
<!-- Categories will be dynamically loaded from API -->
<button class="category-chip active" data-category="all">
All Products
</button>
</div>
</div>
</section>
<!-- Shop Main Content -->
<section class="shop-main" style="padding: 40px 0">
<div class="container">
<div class="shop-layout">
<!-- Products Grid (Main Content) -->
<div class="shop-content" style="width: 100%">
<div class="shop-toolbar">
<p class="results-count" id="resultsCount">Loading products...</p>
<!-- Sort Dropdown -->
<select
class="filter-select"
id="sortSelect"
style="
padding: 8px 12px;
border: 1px solid #e1e8ed;
border-radius: 8px;
"
>
<option value="featured">Featured</option>
<option value="newest">Newest</option>
<option value="price-low">Price: Low to High</option>
<option value="price-high">Price: High to Low</option>
<option value="name">Name: A to Z</option>
</select>
</div>
<div class="products-grid" id="productsGrid">
<!-- Products will be loaded here -->
</div>
<div
id="noProducts"
style="display: none; text-align: center; padding: 40px"
>
<p>No products found.</p>
</div>
<div class="pagination" id="pagination">
<!-- Pagination will be inserted here -->
</div>
</div>
</div>
</div>
</section>
<!-- Footer -->
<footer class="footer">
<div class="container">
<div class="footer-grid">
<div class="footer-col">
<h3 class="footer-title">Sky Art Shop</h3>
<p class="footer-text">
Your destination for unique art pieces and creative supplies.
</p>
<div class="social-links">
<a href="#" class="social-link"><i class="bi bi-facebook"></i></a>
<a href="#" class="social-link"
><i class="bi bi-instagram"></i
></a>
<a href="#" class="social-link"><i class="bi bi-twitter"></i></a>
<a href="#" class="social-link"
><i class="bi bi-pinterest"></i
></a>
</div>
</div>
<div class="footer-col">
<h4 class="footer-heading">Shop</h4>
<ul class="footer-links">
<li><a href="/shop">All Products</a></li>
<li><a href="/shop?category=paintings">Paintings</a></li>
<li><a href="/shop?category=prints">Prints</a></li>
<li><a href="/shop?category=supplies">Art Supplies</a></li>
</ul>
</div>
<div class="footer-col">
<h4 class="footer-heading">About</h4>
<ul class="footer-links">
<li><a href="/about">Our Story</a></li>
<li><a href="/portfolio">Portfolio</a></li>
<li><a href="/blog">Blog</a></li>
<li><a href="/contact">Contact</a></li>
</ul>
</div>
<div class="footer-col">
<h4 class="footer-heading">Customer Service</h4>
<ul class="footer-links">
<li><a href="/shipping-info">Shipping Info</a></li>
<li><a href="/returns">Returns</a></li>
<li><a href="/faq">FAQ</a></li>
<li><a href="/privacy">Privacy Policy</a></li>
</ul>
</div>
</div>
<div class="footer-bottom">
<p>&copy; 2025 Sky Art Shop. All rights reserved.</p>
</div>
</div>
</footer>
<!-- Core Scripts -->
<script src="/assets/js/api-cache.js"></script>
<script src="/assets/js/shop-system.js"></script>
<script src="/assets/js/cart.js"></script>
<script src="/assets/js/api-client.js"></script>
<script src="/assets/js/notifications.js"></script>
<script src="/assets/js/page-transitions.js?v=1767228800"></script>
<script src="/assets/js/back-button-control.js?v=1767228687"></script>
<script src="/assets/js/navigation.js?v=1767228687"></script>
<script>
// Mobile Menu Toggle (Same as other pages)
const mobileMenuToggle = document.getElementById("mobileMenuToggle");
const mobileMenu = document.getElementById("mobileMenu");
const mobileMenuClose = document.getElementById("mobileMenuClose");
if (mobileMenuToggle) {
mobileMenuToggle.addEventListener("click", () => {
mobileMenu.classList.toggle("active");
});
}
if (mobileMenuClose) {
mobileMenuClose.addEventListener("click", () => {
mobileMenu.classList.remove("active");
});
}
// Load and Display Products
let allProducts = [];
let filteredProducts = [];
let currentCategory = "all";
// Load Categories from API
async function loadCategories() {
try {
console.log("Shop page: Loading categories from API...");
const response = await fetch("/api/categories");
const data = await response.json();
console.log("Shop page: Categories API response:", data);
if (data.success && data.categories) {
displayCategoryChips(data.categories);
}
} catch (error) {
console.error("Error loading categories:", error);
}
}
// Display Category Filter Chips
function displayCategoryChips(categories) {
const categoryChipsContainer =
document.querySelector(".category-chips");
if (!categoryChipsContainer) return;
// Build HTML for all categories
let chipsHTML =
'<button class="category-chip active" data-category="all">All Products</button>';
categories.forEach((category) => {
const categorySlug = category.toLowerCase().replace(/\s+/g, "-");
chipsHTML += `<button class="category-chip" data-category="${categorySlug}">${category}</button>`;
});
categoryChipsContainer.innerHTML = chipsHTML;
// Attach event listeners to new chips
attachCategoryListeners();
}
// Attach Category Click Listeners
function attachCategoryListeners() {
document.querySelectorAll(".category-chip").forEach((chip) => {
chip.addEventListener("click", function () {
document
.querySelectorAll(".category-chip")
.forEach((c) => c.classList.remove("active"));
this.classList.add("active");
currentCategory = this.dataset.category;
if (currentCategory === "all") {
filteredProducts = allProducts;
} else {
filteredProducts = allProducts.filter(
(p) =>
p.category?.toLowerCase().replace(/\s+/g, "-") ===
currentCategory
);
}
applySorting();
});
});
}
async function loadProducts() {
try {
console.log("Shop page: Loading products from API...");
const response = await window.apiCache.fetch("/api/products");
const data = await response.json();
console.log("Shop page: API response:", data);
if (data.success) {
allProducts = data.products || data.data || [];
filteredProducts = allProducts;
console.log("Shop page: Loaded", allProducts.length, "products");
// Apply initial sort (featured by default)
applySorting();
} else {
console.error("Shop page: API returned success=false");
}
} catch (error) {
console.error("Error loading products:", error);
document.getElementById("resultsCount").textContent =
"Error loading products";
}
}
// Apply current sorting to filtered products
function applySorting() {
const sortBy =
document.getElementById("sortSelect")?.value || "featured";
let sorted = [...filteredProducts];
switch (sortBy) {
case "featured":
// Sort by featured first, then by best seller, then by newest
sorted.sort((a, b) => {
// Featured products first
if (a.isfeatured && !b.isfeatured) return -1;
if (!a.isfeatured && b.isfeatured) return 1;
// Then best sellers
if (a.isbestseller && !b.isbestseller) return -1;
if (!a.isbestseller && b.isbestseller) return 1;
// Then by newest
return new Date(b.createdat) - new Date(a.createdat);
});
break;
case "price-low":
sorted.sort((a, b) => parseFloat(a.price) - parseFloat(b.price));
break;
case "price-high":
sorted.sort((a, b) => parseFloat(b.price) - parseFloat(a.price));
break;
case "name":
sorted.sort((a, b) => a.name.localeCompare(b.name));
break;
case "newest":
sorted.sort(
(a, b) => new Date(b.createdat) - new Date(a.createdat)
);
break;
}
displayProducts(sorted);
updateResultsCount(sorted.length);
}
function displayProducts(products) {
const grid = document.getElementById("productsGrid");
const noProducts = document.getElementById("noProducts");
console.log(
"Shop page: displayProducts called with",
products.length,
"products"
);
if (products.length === 0) {
grid.style.display = "none";
noProducts.style.display = "block";
console.log("Shop page: No products to display");
return;
}
grid.style.display = "grid";
noProducts.style.display = "none";
console.log("Shop page: Rendering product cards...");
grid.innerHTML = products
.map((product) => {
// Get the primary image from images array
let productImage = "/assets/images/placeholder.svg";
if (
product.images &&
Array.isArray(product.images) &&
product.images.length > 0
) {
// Find primary image or use first one
const primaryImg = product.images.find((img) => img.is_primary);
productImage = primaryImg
? primaryImg.image_url
: product.images[0].image_url;
} else if (product.imageurl) {
// Fallback to old imageurl field
productImage = product.imageurl;
}
// Build badges HTML
let badges = "";
if (product.isfeatured) {
badges +=
'<span class="badge-featured"><i class="bi bi-star-fill"></i> Featured</span>';
}
if (product.isbestseller) {
badges +=
'<span class="badge-bestseller"><i class="bi bi-trophy-fill"></i> Best Seller</span>';
}
return `
<div class="product-card">
${badges ? `<div class="product-badges">${badges}</div>` : ""}
<a href="/product?id=${product.id}" class="product-link">
<div class="product-image">
<img src="${productImage}" alt="${
product.name
}" loading="lazy" onerror="this.src='/assets/images/placeholder.svg'" />
</div>
</a>
<div class="product-info">
<a href="/product?id=${product.id}" class="product-title-link">
<h3>${product.name}</h3>
</a>
${
product.shortdescription || product.description
? `<p class="product-description">${
product.shortdescription ||
(product.description
? product.description
.replace(/<[^>]*>/g, "")
.substring(0, 60)
: "")
}</p>`
: ""
}
${
product.category
? `<p class="product-category"><i class="bi bi-tag-fill"></i> ${product.category}</p>`
: ""
}
<div class="product-footer">
<p class="price">$${parseFloat(product.price).toFixed(2)}</p>
${
product.stockquantity > 0
? '<p class="stock-status in-stock"><i class="bi bi-check-circle-fill"></i> In Stock</p>'
: '<p class="stock-status out-of-stock"><i class="bi bi-x-circle-fill"></i> Out of Stock</p>'
}
</div>
</div>
<div class="product-actions">
<button class="btn btn-small btn-icon"
onclick="event.stopPropagation(); addToWishlist('${
product.id
}', '${product.name.replace(/'/g, "\\'")}', ${
product.price
}, '${productImage.replace(/'/g, "\\'")}')"
aria-label="Add to wishlist">
<i class="bi bi-heart"></i>
</button>
<button class="btn btn-small btn-icon"
onclick="event.stopPropagation(); addToCart('${
product.id
}', '${product.name.replace(/'/g, "\\'")}', ${
product.price
}, '${productImage.replace(/'/g, "\\'")}')"
aria-label="Add to cart"
${
product.stockquantity <= 0
? 'disabled style="opacity: 0.5; cursor: not-allowed;"'
: ""
}>
<i class="bi bi-cart-plus"></i>
</button>
</div>
</div>
`;
})
.join("");
}
function updateResultsCount(count) {
document.getElementById(
"resultsCount"
).textContent = `Showing ${count} product${count !== 1 ? "s" : ""}`;
}
// Category Filter - Now handled by attachCategoryListeners()
// Sort
document
.getElementById("sortSelect")
?.addEventListener("change", function () {
applySorting();
});
// Price Filter
document
.getElementById("applyPriceFilter")
?.addEventListener("click", function () {
const min =
parseFloat(document.getElementById("priceMin").value) || 0;
const max =
parseFloat(document.getElementById("priceMax").value) || Infinity;
filteredProducts = allProducts.filter((p) => {
const price = parseFloat(p.price);
return price >= min && price <= max;
});
applySorting();
});
// Simple Cart and Wishlist Functions
function addToCart(productId, name, price, imageurl) {
window.ShopSystem.addToCart(
{
id: String(productId),
name,
price: parseFloat(price),
imageurl,
},
1
);
}
function addToWishlist(productId, name, price, imageurl) {
window.ShopSystem.addToWishlist({
id: String(productId),
name,
price: parseFloat(price),
imageurl,
});
}
function showNotification(message, type = "info") {
const notification = document.createElement("div");
notification.className = `notification notification-${type}`;
notification.textContent = message;
notification.style.cssText = `
position: fixed;
top: 80px;
right: 20px;
background: ${
type === "success"
? "#10b981"
: type === "error"
? "#ef4444"
: "#3b82f6"
};
color: white;
padding: 12px 24px;
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
z-index: 10000;
animation: slideIn 0.3s ease;
`;
document.body.appendChild(notification);
setTimeout(() => {
notification.style.animation = "slideOut 0.3s ease";
setTimeout(() => notification.remove(), 300);
}, 3000);
}
// Search functionality
document
.getElementById("productSearch")
?.addEventListener("input", function (e) {
const searchTerm = e.target.value.toLowerCase();
if (searchTerm === "") {
filteredProducts = allProducts;
} else {
filteredProducts = allProducts.filter(
(p) =>
p.name.toLowerCase().includes(searchTerm) ||
(p.description &&
p.description.toLowerCase().includes(searchTerm))
);
}
displayProducts(filteredProducts);
updateResultsCount(filteredProducts.length);
});
// Initialize
loadCategories();
loadProducts();
</script>
</body>
</html>