|
Server IP : 217.21.85.138 / Your IP : 216.73.216.103 Web Server : LiteSpeed System : Linux in-mum-web906.main-hosting.eu 4.18.0-553.37.1.lve.el8.x86_64 #1 SMP Mon Feb 10 22:45:17 UTC 2025 x86_64 User : u915722082 ( 915722082) PHP Version : 7.4.33 Disable Function : system, exec, shell_exec, passthru, mysql_list_dbs, ini_alter, dl, symlink, link, chgrp, leak, popen, apache_child_terminate, virtual, mb_send_mail MySQL : OFF | cURL : ON | WGET : ON | Perl : OFF | Python : OFF Directory (0755) : /home/u915722082/.nvm/../public_html/lohri/user/ |
| [ Home ] | [ C0mmand ] | [ Upload File ] |
|---|
<!-- shop.php -->
<?php
if (session_status() == PHP_SESSION_NONE) {
session_start();
}
require_once __DIR__ . '/../config/config.php';
try {
$pdo = new PDO(
"mysql:host=" . DB_HOST . ";dbname=" . DB_NAME . ";charset=utf8mb4",
DB_USER,
DB_PASS,
[
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
]
);
} catch (PDOException $e) {
die("❌ Database connection failed: " . htmlspecialchars($e->getMessage()));
}
// Handle URL parameters for initial filtering
// Handle URL parameters for initial filtering
// Handle URL parameters for initial filtering
$initialFilters = [];
if (isset($_GET['category'])) {
$initialFilters['category'] = [$_GET['category']];
}
if (isset($_GET['search'])) {
$initialFilters['search'] = $_GET['search'];
}
if (isset($_GET['tag'])) {
$initialFilters['tag'] = $_GET['tag'];
}
if (isset($_GET['brand'])) {
$brandParam = $_GET['brand'];
$initialFilters['brand'] = is_array($brandParam) ? $brandParam : [$brandParam];
}
if (isset($_GET['collection'])) {
$initialFilters['collection'] = $_GET['collection'];
}
// Get all unique filter options from product_variation_options
function getFilterOptions($pdo) {
$sql = "SELECT DISTINCT option_name FROM product_variation_options ORDER BY option_name";
$stmt = $pdo->query($sql);
return $stmt->fetchAll(PDO::FETCH_COLUMN);
}
// Get option values for a specific option
function getOptionValues($pdo, $optionName) {
$sql = "SELECT DISTINCT option_value FROM product_variation_options WHERE option_name = ? ORDER BY option_value";
$stmt = $pdo->prepare($sql);
$stmt->execute([$optionName]);
return $stmt->fetchAll(PDO::FETCH_COLUMN);
}
// Get price range from product_variations
function getPriceRange($pdo) {
$sql = "SELECT MIN(price) as min_price, MAX(price) as max_price FROM product_variations WHERE price > 0";
$stmt = $pdo->query($sql);
return $stmt->fetch(PDO::FETCH_ASSOC);
}
// Get categories from products table
function getCategories($pdo) {
$sql = "SELECT DISTINCT category FROM products WHERE category IS NOT NULL AND category != '' ORDER BY category";
$stmt = $pdo->query($sql);
return $stmt->fetchAll(PDO::FETCH_COLUMN);
}
$filterOptions = getFilterOptions($pdo);
$priceRange = getPriceRange($pdo);
$categories = getCategories($pdo);
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Product Filter UI</title>
<?php include "../user/header.php" ?>
<style>
/* ============================================ */
/* MODAL OFFER BADGE STYLING */
/* ============================================ */
#modalOfferBadge strong {
color: #28a745 !important;
font-weight: 700;
font-size: 16px !important;
}
#modalOfferBadge small {
color: white !important;
font-size: 13px !important;
}
#modalOfferBadge span {
color: #28a745 !important;
font-weight: 600 !important;
font-size: 16px !important;
}
/* Offer Price in Main Price Display */
.modal-product-price {
display: flex;
align-items: baseline;
gap: 12px;
margin-bottom: 20px;
}
.modal-product-price .current-price {
font-size: 32px;
font-weight: 700;
color: #000;
}
/* When it's an offer price */
.modal-product-price .current-price[style*="color: #667eea"] {
color: #667eea !important;
animation: priceHighlight 2s ease-in-out infinite;
}
.modal-product-price .original-price {
text-decoration: line-through;
color: #999;
font-size: 20px;
font-weight: 500;
}
/* Pulse animation for offer prices */
@keyframes priceHighlight {
0%, 100% {
opacity: 1;
}
50% {
opacity: 0.7;
}
}
/* Make sure the badge is visible when displayed */
.alert.alert-success#modalOfferBadge {
display: none; /* Hidden by default */
}
.alert.alert-success#modalOfferBadge[style*="display: block"] {
display: block !important;
}
</style>
<style>
@media (min-width: 1200px) and (max-width: 1599px) {
.product-image {
width: 100% !important; /* Adjust as needed */
height: 500px !important;
object-fit: cover !important; /* Optional: ensures the image fills the height nicely */
}
.insta-highlight-section{
padding: 0px 20px 150px !important;
}
.nw-slider-nav-btn {
position: absolute;
top: 40% !important;
}
}
/* Wishlist Active State for Product Cards */
.btn-wishlist-overlay.in-wishlist {
background: #000 !important;
}
.btn-wishlist-overlay.in-wishlist svg {
stroke: #fff !important;
fill: #fff !important;
}
/* Add this CSS rule */
html {
scroll-behavior: smooth;
}
body {
overflow-x: hidden;
}
.offcanvas-backdrop {
z-index: 1040;
background: unset !important;
}
.product-title a {
color:#666666;
font-size:13px
}
.offcanvas {
z-index: 1045;
transition: transform 0.3s ease-in-out;
}
/* Ensure proper backdrop behavior */
.offcanvas-backdrop {
z-index: 1040;
}
.offcanvas.show {
visibility: visible;
}
.offcanvas:not(.show) {
visibility: hidden;
}
/* Ensure overlay disappears when offcanvas is closed */
body:not(.offcanvas-open) .offcanvas-backdrop {
display: none !important;
}
.breadcrumb-nav {
/* background-color: #f8f9fa; */
padding: 2rem 0;
/* border-bottom: 1px solid #dee2e6; */
font-size:12px
}
.breadcrumb {
margin-bottom: 0;
}
.breadcrumb-item a {
color: #6c757d;
text-decoration: none;
font-size:12px
}
.products-header {
/* padding: 2rem 0 1rem 0; */
}
.filter-controls {
padding: 1rem 0;
border-bottom: unset;
}
.filter-btn {
background: none;
border: 1px solid #dee2e6;
padding: 0.5rem 1rem;
border-radius: 4px;
display: flex;
align-items: center;
gap: 0.5rem;
font-size: 0.9rem;
color: black !important;
}
.sort-select {
border:unset;
border-left: 1px solid #ccc;
padding: .375rem 4.25rem .375rem .75rem;
/* border-radius: 4px; */
background: white;
color: black !important;
/* Remove default arrow */
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
/* Add custom SVG background */
background-image: url("data:image/svg+xml,%3Csvg class='icon icon-caret' xmlns='http://www.w3.org/2000/svg' width='53' height='52' viewBox='0 0 33 32' fill='none'%3E%3Cpath d='M15.8314 20.4186V9H17.1732V20.4186L22.5473 15.0446L23.5 16L16.5 23L9.5 16L10.4527 15.0446L15.8314 20.4186Z' fill='%23000000' stroke-width='0.2'%3E%3C/path%3E%3C/svg%3E");
background-repeat: no-repeat;
background-position: right 0.75rem center;
background-size: 30px 30px; /* Increase size here */
/* Ensure proper cursor */
cursor: pointer;
}
/* Focus state */
.sort-select:focus {
outline: none;
border-color: #000;
box-shadow: 0 0 0 0.2rem rgba(0, 0, 0, 0.25);
}
/* Hover state */
.sort-select:hover {
border-color: #999;
}
/* For better cross-browser support */
.sort-select::-ms-expand {
display: none; /* Hide default arrow in IE */
}
.offcanvas-filter {
width: 380px !important;
}
.filter-section {
border-bottom: 1px solid #dee2e6;
padding: 1.5rem 0;
}
.filter-section:last-child {
border-bottom: none;
}
.filter-title {
font-weight: 600;
margin-bottom: 1rem;
display: flex;
justify-content: space-between;
align-items: center;
}
.reset-link {
color: #6c757d;
text-decoration: underline;
font-size: 0.9rem;
cursor: pointer;
}
.form-check {
margin-bottom: 0.75rem;
}
.form-check-input {
border-radius: 3px;
}
.price-range {
margin: 1rem 0;
}
.price-inputs {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 1rem;
margin-top: 1rem;
}
.price-input {
background: #f8f9fa;
border: 1px solid #dee2e6;
padding: 0.5rem;
border-radius: 4px;
text-align: center;
width: 105px;
}
.price-slider {
width: 100%;
height: 4px;
border-radius: 2px;
background: #dee2e6;
outline: none;
-webkit-appearance: none;
position: relative;
}
.price-slider::-webkit-slider-thumb {
appearance: none;
width: 18px;
height: 18px;
border-radius: 50%;
background: #000;
cursor: pointer;
}
.color-option {
display: flex;
align-items: center;
gap: 0.5rem;
}
.color-dot {
width: 12px;
height: 12px;
border-radius: 50%;
border: 1px solid #dee2e6;
}
.color-beige { background-color: #F5F5DC; }
.color-brown { background-color: #8B4513; }
.color-green { background-color: #228B22; }
.size-options {
display: flex;
gap: 0.5rem;
}
.size-btn {
width: 40px;
height: 40px;
border: 1px solid #dee2e6;
background: white;
border-radius: 4px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: all 0.2s;
font-size:13px;
color: black ;
}
.size-btn:hover, .size-btn.active {
border-color: #000;
background: #000;
color: white;
}
.apply-filters-btn {
background: #000;
color: white;
border: none;
padding: 0.75rem;
border-radius: 4px;
width: 100%;
margin-bottom: 1rem;
}
.clear-all-btn {
background: none;
border: none;
color: #6c757d;
text-decoration: underline;
padding: 0;
cursor: pointer;
}
/* Filter tags styles */
.filter-tags-container {
padding: 1rem 0;
border-bottom: 1px solid #dee2e6;
display: none;
}
.filter-tag {
display: inline-flex;
align-items: center;
background: #f2f2f2;
border: 1px solid #dee2e6;
border-radius: 5px;
padding: 0.70rem 0.75rem;
margin: 0.25rem;
font-size: 14px;
gap: 0.5rem;
color:black
}
.filter-tag.clear-all-tag {
background: #000;
color: white;
border-color: #000;
cursor: pointer;
}
.remove-tag {
background: none;
border: none;
color: #6c757d;
font-size: 1.2rem;
line-height: 1;
padding: 0;
cursor: pointer;
}
.remove-tag:hover {
color: #000;
}
.container-fluid, .container-lg, .container-md, .container-sm, .container-xl {
padding-left: 30px;
padding-right: 30px;
}
.price-range-container {
position: relative;
height: 40px;
margin: 15px 0;
}
.price-slider-track {
position: absolute;
top: 18px;
left: 0;
right: 0;
height: 4px;
background: #dee2e6;
border-radius: 2px;
}
.price-slider-range {
position: absolute;
height: 100%;
background: #000;
border-radius: 2px;
}
.price-slider {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 40px;
background: transparent;
pointer-events: none;
-webkit-appearance: none;
appearance: none;
}
.price-slider::-webkit-slider-thumb {
appearance: none;
width: 18px;
height: 18px;
border-radius: 50%;
background: #000;
cursor: pointer;
pointer-events: all;
position: relative;
z-index: 2;
}
.price-slider::-moz-range-thumb {
width: 18px;
height: 18px;
border-radius: 50%;
background: #000;
cursor: pointer;
pointer-events: all;
border: none;
}
.products-grid__overlay {
position: fixed;
inset: 0;
z-index: 1040;
width: 100%;
height: 100%;
background-color: rgba(var(#0000001a), 0.1);
backdrop-filter: blur(2px);
opacity: 0;
visibility: hidden;
pointer-events: none;
transition: opacity 0.3s ease, visibility 0.3s ease;
}
.products-grid__overlay.show {
opacity: 1;
visibility: visible;
pointer-events: auto;
}
@media (max-width: 767px) {
.breadcrumb-nav{
padding: 1rem 0 0 0;
}
.sort-select,.filter-btn{
font-size:13px ;
}
.product-card {
border-top: 1px solid #e5e7eb; /* ✅ Keep top border */
border-radius: 0 !important; /* Remove curves */
}
/* Images - no border-radius */
.product-image {
height: 250px !important;
border-radius: 0 !important; /* ✅ Sharp corners */
border: none !important;
}
.product-image .main-image,
.product-image .hover-image {
border-radius: 0 !important; /* ✅ Sharp corners */
}
.product-image .main-image, .product-image .hover-image {
border-radius: unset !important;
}
.product-info{
padding: 11px 0 !important;
}
.product-title {
margin-bottom: 6px !important;
}
.pb-smm-20{
padding-bottom: 40px !important;
}
.mt-smm-0{
margin-top: 0px !important;
}
.product-actions {
position: absolute;
bottom: 12px !important;
}
.product-actions button{
font-size: 13px !important;
}
}
/* Toggle Switches */
.condition-filters {
display: flex;
align-items: center;
gap: 20px;
}
.condition-toggle-wrapper {
display: flex;
align-items: center;
gap: 10px;
}
.condition-label {
font-size: 14px;
color: #000;
font-weight: 500;
white-space: nowrap;
}
.condition-toggle {
position: relative;
display: inline-block;
width: 47px;
height: 23px;
cursor: pointer;
}
.condition-toggle input[type="checkbox"] {
opacity: 0;
width: 0;
height: 0;
position: absolute;
}
.toggle-slider {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #ccc;
border-radius: 34px;
transition: all 0.3s ease;
}
.toggle-slider:before {
content: "";
position: absolute;
height: 15px;
width: 15px;
left: 5px;
bottom: 4px;
background-color: white;
border-radius: 50%;
transition: all 0.3s ease;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
}
.condition-toggle input:checked + .toggle-slider {
background-color: black;
}
.condition-toggle input:checked + .toggle-slider:before {
transform: translateX(22px);
}
</style>
<style>
</style>
</head>
<body>
<?php include "../ui/nav.php" ?>
<div class="products-grid__overlay"></div>
<!-- Breadcrumb -->
<div class="breadcrumb-nav">
<div class="container-fluid cf-sm-pl-10">
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="#">Home</a></li>
<li class="breadcrumb-item active" aria-current="page">Products</li>
</ol>
</nav>
</div>
</div>
<!-- Main Content -->
<div class="container-fluid cf-sm-pl-10 pb-smm-20" style="padding-bottom:150px">
<!-- Products Header -->
<div class="products-header text-center d-none d-sm-inline">
<h1>Products</h1>
</div>
<!-- Filter Controls -->
<div class="filter-controls d-flex justify-content-between align-items-center flex-wrap gap-3">
<div class="d-flex align-items-center gap-3">
<button class="filter-btn" data-bs-toggle="offcanvas" data-bs-target="#filterOffcanvas">
<i class="fas fa-sliders-h"></i>
Filters
</button>
<!-- Product Condition Toggles -->
<div class="condition-filters d-flex align-items-center gap-3">
<div class="condition-toggle-wrapper">
<label class="condition-toggle">
<input type="checkbox" id="preLovedToggle" data-condition="pre_loved">
<span class="toggle-slider"></span>
</label>
<span class="condition-label">Pre-loved</span>
</div>
<div class="condition-toggle-wrapper">
<label class="condition-toggle">
<input type="checkbox" id="brandNewToggle" data-condition="brand_new">
<span class="toggle-slider"></span>
</label>
<span class="condition-label">Brand new</span>
</div>
</div>
</div>
<select class="sort-select" id="sortSelect">
<option value="name_asc">Alphabetically, A-Z</option>
<option value="name_desc">Alphabetically, Z-A</option>
<option value="price_asc">Price, low to high</option>
<option value="price_desc">Price, high to low</option>
<option value="newest">Newest First</option>
</select>
</div>
<!-- Filter Tags -->
<div id="filterTags" class="filter-tags-container">
<div class="d-flex flex-wrap align-items-center">
<!-- Tags will be inserted here dynamically -->
</div>
</div>
<!-- Products Container -->
<div id="productsContainer" class="row mt-4 g-2 g-sm-3">
</div>
<!-- Loading indicator -->
<div id="loadingIndicator" class="text-center mt-4" style="display: none;">
<div class="spinner-border" role="status">
<span class="visually-hidden">Loading...</span>
</div>
</div>
</div>
<!-- Filter Sidebar -->
<div class="offcanvas offcanvas-start offcanvas-filter" tabindex="-1" id="filterOffcanvas">
<div class="offcanvas-header border-bottom">
<h5 class="offcanvas-title">Filters</h5>
<button type="button" class="btn-close" data-bs-dismiss="offcanvas">
<!--<svg class="icon icon-close" width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">-->
<!-- <path d="M7.99989 9.31163L2.29175 15.0198L0.980116 13.7081L6.68826 8L0.980116 2.29186L2.29175 0.980222L7.99989 6.68837L13.708 0.980222L15.0197 2.29186L9.31153 8L15.0197 13.7081L13.708 15.0198L7.99989 9.31163Z" fill="currentColor"></path>-->
<!--</svg>-->
</button>
</div>
<div class="offcanvas-body d-flex flex-column">
<div class="flex-grow-1">
<!-- SIZE FILTER -->
<?php
$sizeValues = getOptionValues($pdo, 'size');
if (!empty($sizeValues)):
?>
<div class="filter-section">
<div class="filter-title">
<span>Size</span>
<span class="reset-link" data-filter="size">Reset</span>
</div>
<div class="size-options">
<?php foreach($sizeValues as $value): ?>
<button class="size-btn" data-size="<?php echo $value; ?>"
data-filter="size" data-value="<?php echo $value; ?>">
<?php echo $value; ?>
</button>
<?php endforeach; ?>
</div>
</div>
<?php endif; ?>
<!-- PRICE FILTER -->
<div class="filter-section">
<div class="filter-title">
<span>Price</span>
<span class="reset-link" data-filter="price">Reset</span>
</div>
<div class="price-range-container">
<div class="price-slider-track">
<div class="price-slider-range"></div>
</div>
<input type="range" class="price-slider price-slider-min" min="<?php echo $priceRange['min_price']; ?>"
max="<?php echo $priceRange['max_price']; ?>" value="<?php echo $priceRange['min_price']; ?>"
id="priceSliderMin">
<input type="range" class="price-slider price-slider-max" min="<?php echo $priceRange['min_price']; ?>"
max="<?php echo $priceRange['max_price']; ?>" value="<?php echo $priceRange['max_price']; ?>"
id="priceSliderMax">
</div>
<div class="price-inputs">
<div>
<label class="form-label small text-muted">From</label>
<input type="text" class="price-input price-from"
value="₹<?php echo number_format($priceRange['min_price']); ?>" readonly>
</div>
<div>
<label class="form-label small text-muted">To</label>
<input type="text" class="price-input price-to"
value="₹<?php echo number_format($priceRange['max_price']); ?>" readonly>
</div>
</div>
</div>
<!-- BRAND FILTER -->
<?php
$brandValues = getOptionValues($pdo, 'brand');
if (!empty($brandValues)):
?>
<div class="filter-section">
<div class="filter-title">
<span>Brand</span>
<span class="reset-link" data-filter="brand">Reset</span>
</div>
<?php foreach($brandValues as $value): ?>
<div class="form-check">
<input class="form-check-input filter-checkbox" type="checkbox"
id="brand_<?php echo md5($value); ?>"
data-filter="brand" data-value="<?php echo $value; ?>">
<label class="form-check-label" for="brand_<?php echo md5($value); ?>">
<?php echo $value; ?>
</label>
</div>
<?php endforeach; ?>
</div>
<?php endif; ?>
<!-- CATEGORY FILTER -->
<?php if(!empty($categories)): ?>
<div class="filter-section">
<div class="filter-title">
<span>Category</span>
<span class="reset-link" data-filter="category">Reset</span>
</div>
<?php foreach($categories as $category): ?>
<div class="form-check">
<input class="form-check-input filter-checkbox" type="checkbox"
id="cat_<?php echo md5($category); ?>"
data-filter="category" data-value="<?php echo htmlspecialchars($category); ?>">
<label class="form-check-label" for="cat_<?php echo md5($category); ?>">
<?php echo htmlspecialchars($category); ?>
</label>
</div>
<?php endforeach; ?>
</div>
<?php endif; ?>
<!-- COLOR FILTER -->
<?php
$colorValues = getOptionValues($pdo, 'color');
if (!empty($colorValues)):
?>
<div class="filter-section">
<div class="filter-title">
<span>Color</span>
<span class="reset-link" data-filter="color">Reset</span>
</div>
<?php foreach($colorValues as $value): ?>
<div class="form-check">
<input class="form-check-input filter-checkbox" type="checkbox"
id="color_<?php echo md5($value); ?>"
data-filter="color" data-value="<?php echo $value; ?>">
<label class="form-check-label color-option" for="color_<?php echo md5($value); ?>">
<span class="color-dot" style="background-color: <?php echo strtolower($value); ?>"></span>
<?php echo $value; ?>
</label>
</div>
<?php endforeach; ?>
</div>
<?php endif; ?>
<!-- OTHER DYNAMIC FILTERS -->
<?php
foreach($filterOptions as $option):
if (in_array(strtolower($option), ['size', 'brand', 'color'])) continue;
$optionValues = getOptionValues($pdo, $option);
if (empty($optionValues)) continue;
?>
<div class="filter-section">
<div class="filter-title">
<span><?php echo ucfirst($option); ?></span>
<span class="reset-link" data-filter="<?php echo $option; ?>">Reset</span>
</div>
<?php foreach($optionValues as $value): ?>
<div class="form-check">
<input class="form-check-input filter-checkbox" type="checkbox"
id="<?php echo strtolower($option . '_' . md5($value)); ?>"
data-filter="<?php echo $option; ?>" data-value="<?php echo $value; ?>">
<label class="form-check-label" for="<?php echo strtolower($option . '_' . md5($value)); ?>">
<?php echo $value; ?>
</label>
</div>
<?php endforeach; ?>
</div>
<?php endforeach; ?>
</div>
<!-- ONLY Clear All Button - Apply button removed -->
<div class="border-top pt-3 mt-auto">
<button class="clear-all-btn d-block mx-auto" id="clearAllFilters" style="padding: 0.75rem; width: 100%; background: black;border-radius: 4px; color: white; text-decoration: none;">
Clear all
</button>
</div>
</div>
</div>
<div class="modal fade" id="variantSelectionModal" tabindex="-1">
<div class="modal-dialog modal-lg modal-dialog-centered modal-dialogg">
<div class="modal-content">
<div class="modal-header border-0">
<button type="button" class="btn-close bc1" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body p-0">
<div class="row g-0">
<!-- Left Side - Product Image -->
<div class="col-md-6">
<div class="variant-modal-image-wrapper" id="variantModalImageWrapper">
<div class="variant-modal-image-display"></div>
<img class="variant-modal-img-element" id="variantModalImage" src="" alt="Product" loading="eager">
</div>
</div>
<!-- Right Side - Product Info -->
<div class="col-md-6">
<div class="modal-product-info p-5">
<div class="modal-product-category" id="modalProductCategory"></div>
<h5 class="modal-product-title" id="modalProductName"></h5>
<div class="modal-product-price" id="modalProductPrice"></div>
<!-- ✅ THESE IDs ARE NOW UNIQUE (only in variant modal) -->
<div id="modalOfferBadge" style="display: none;" class="py-2 mb-3">
<small><strong>Special Offer:</strong> <span id="modalOfferPrice"></span></small>
</div>
<!-- Dynamic Variant Selectors -->
<div id="variantSelectorsContainer"></div>
<!-- Stock Status -->
<div class="modal-stock-status" id="modalStockStatus"></div>
<!-- Quantity Controls -->
<div class="modal-quantity-section">
<label class="form-label fw-semibold">Quantity</label>
<div class="quantity-controls">
<button type="button" class="quantity-btn" id="modalQtyDec">−</button>
<input type="number" id="modalQuantity" class="quantity-input" value="1" min="1">
<button type="button" class="quantity-btn" id="modalQtyInc">+</button>
</div>
</div>
<!-- Action Buttons -->
<div class="modal-action-buttons">
<button type="button" class="btn btn-dark w-100 mb-2" id="modalAddToCartBtn">
Add to Cart
</button>
<button type="button" class="btn btn-outline-dark w-100" id="modalAddToWishlistBtn">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z"/>
</svg>
Add to Wishlist
</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- COMPACT MODAL - PROPER WIDTH & NO GAPS -->
<style>
/* ============================================ */
/* MODAL - IMAGE + INFO SIDE BY SIDE */
/* ============================================ */
.modal-dialogg {
max-width: 1000px !important;
margin: 1.75rem auto !important;
}
.modal-content {
border: none;
border-radius: 0 !important;
overflow: hidden;
position: relative; /* ✅ NEW */
}
.modal-header {
position: absolute;
top: 0;
right: 0;
z-index: 10;
border: none;
background: transparent;
padding: 20px;
pointer-events: none;
}
.bc1 {
/*background: rgba(255, 255, 255, 0.9);*/
border-radius: 50%;
width: 40px;
height: 40px;
display: flex;
align-items: center;
justify-content: center;
opacity: 0.8;
transition: all 0.3s ease;
pointer-events: auto;
}
.bc1:hover {
opacity: 1;
transform: rotate(90deg);
/*background: white;*/
}
/* ============================================ */
/* IMAGE SIDE (LEFT) */
/* ============================================ */
.modal-body {
position: relative;
z-index: 1;
}
/* ============================================ */
/* VARIANT MODAL IMAGE - NEW APPROACH */
/* ============================================ */
.variant-modal-image-wrapper {
width: 100%;
height: 700px;
min-height: 500px;
background: #f5f5f5;
position: relative;
overflow: hidden;
}
.variant-modal-image-display {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-size: cover;
background-position: center;
background-repeat: no-repeat;
z-index: 1;
}
.variant-modal-img-element {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
object-fit: cover;
object-position: center;
z-index: 2;
}
/* Mobile responsive */
@media (max-width: 768px) {
.variant-modal-image-wrapper {
height: 400px;
min-height: 300px;
}
}
/* ============================================ */
/* INFO SIDE (RIGHT) */
/* ============================================ */
.modal-product-info {
padding: 40px;
height: 700px;
overflow-y: auto;
background: white;
}
.modal-product-category {
display: inline-block;
background: #f2f2f2;
color: #000;
padding: 6px 16px;
border-radius: 4px;
font-size: 11px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.5px;
margin-bottom: 12px;
}
.modal-product-title {
font-size: 24px;
font-weight: 600;
margin-bottom: 8px;
color: #000;
line-height: 1.3;
}
.modal-product-price {
font-size: 32px;
font-weight: 700;
margin-bottom: 0px;
color: #000;
display: flex;
align-items: baseline;
gap: 12px;
}
.modal-product-price .current-price {
color: #000;
}
.modal-product-price .original-price {
text-decoration: line-through;
color: #999;
font-size: 20px;
font-weight: 500;
}
/* Offer Badge */
#modalOfferBadge strong,
#modalOfferBadge small,
#modalOfferBadge span {
color: white;
}
/* ============================================ */
/* VARIANT SELECTORS */
/* ============================================ */
#variantSelectorsContainer {
margin-bottom: 20px;
}
.variant-selector-group {
margin-bottom: 24px;
}
.variant-selector-label {
font-weight: 600;
font-size: 14px;
margin-bottom: 12px;
display: block;
text-transform: uppercase;
letter-spacing: 0.5px;
color: #000;
}
/* Color Selector */
.color-selector {
display: flex;
gap: 12px;
flex-wrap: wrap;
}
.color_option {
width: 25px;
height: 25px;
border-radius: 50%;
border: 1px solid #ddd;
cursor: pointer;
position: relative;
transition: all 0.25s ease;
background-color: var(--color-value, #ccc);
}
.color_option::after {
content: '';
position: absolute;
inset: -5px;
border: 1px solid transparent;
border-radius: 50%;
transition: border-color 0.25s ease;
}
.color_option.selected::after {
border-color: #000;
}
.color_option:hover {
transform: scale(1.05);
}
/* Size Selector */
.size-selector {
display: flex;
gap: 10px;
flex-wrap: wrap;
}
.size-option {
min-width: 60px;
padding: 7px 7px;
border: 1px solid #ccc;
border-radius: 4px;
background: white;
cursor: pointer;
transition: all 0.25s ease;
font-size: 14px;
font-weight: 500;
color: #333;
text-align: center;
}
.size-option:hover {
border-color: #000;
transform: translateY(-2px);
}
.size-option.selected {
border-color: #000;
background: #000;
color: white;
}
/* ============================================ */
/* STOCK STATUS */
/* ============================================ */
.modal-stock-status {
display: inline-flex;
align-items: center;
gap: 8px;
padding: 10px 16px;
border-radius: 8px;
font-size: 14px;
font-weight: 500;
margin-bottom: 20px;
}
.modal-stock-status.in-stock {
background: #d1fae5;
color: #065f46;
}
.modal-stock-status.out-of-stock {
background: #fee2e2;
color: #991b1b;
}
/* ============================================ */
/* QUANTITY CONTROLS */
/* ============================================ */
.modal-quantity-section {
margin-bottom: 24px;
}
.modal-quantity-section .form-label {
font-size: 12px;
font-weight: 700;
margin-bottom: 10px;
text-transform: uppercase;
letter-spacing: 0.5px;
color: #000;
}
.quantity-controls {
display: inline-flex;
align-items: center;
border: 1px solid #ddd;
/*border-radius: 6px;*/
overflow: hidden;
margin-left: 20px;
background: white;
}
.quantity-btn {
width: 45px;
height: 45px;
border: none;
background: transparent;
font-size: 20px;
cursor: pointer;
transition: background 0.2s ease;
color: #333;
}
.quantity-btn:hover {
background: #f5f5f5;
}
.quantity-input {
width: 70px;
height: 45px;
text-align: center;
border: none;
font-weight: 500;
font-size: 16px;
color: #000;
}
.quantity-input:focus {
outline: none;
background: #fafafa;
}
.quantity-input::-webkit-inner-spin-button,
.quantity-input::-webkit-outer-spin-button {
-webkit-appearance: none;
margin: 0;
}
/* ============================================ */
/* ACTION BUTTONS */
/* ============================================ */
.modal-action-buttons {
margin-top: 24px;
}
.modal-action-buttons button {
font-weight: 500;
padding: 16px 24px;
border-radius: 8px;
font-size: 15px;
transition: all 0.3s ease;
display: flex;
align-items: center;
justify-content: center;
gap: 10px;
width: 100%;
border: none;
}
.btn-dark {
background: #000;
color: white;
margin-bottom: 12px;
}
.btn-dark:hover {
background: #333;
transform: translateY(-2px);
}
.btn-dark:disabled {
background: #999;
cursor: not-allowed;
transform: none;
}
.btn-outline-dark {
background: white;
border: 1px solid #000 !important;
color: black;
}
.btn-outline-dark:hover {
background: #000;
color:white;
transform: translateY(-2px);
}
.modal-action-buttons button.wishlist-active {
background: #10b981 !important;
border-color: #10b981 !important;
color: white !important;
}
.modal-action-buttons button.wishlist-active:hover {
background: #059669 !important;
}
.modal-action-buttons button.wishlist-active svg {
fill: white;
stroke: white;
}
/* ============================================ */
/* MOBILE RESPONSIVE */
/* ============================================ */
@media (max-width: 768px) {
.modal-dialogg {
max-width: 100%;
margin: 0;
}
.modal-content {
min-height: 100vh;
}
.modal-product-image {
height: 400px;
}
.modal-product-info {
padding: 24px;
height: auto;
}
.modal-product-title {
font-size: 20px;
}
.modal-product-price {
font-size: 26px;
}
}
</style>
<?php include "../ui/footer.php" ?>
<script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.0/js/bootstrap.bundle.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Swiper/11.0.5/swiper-bundle.min.js"></script>
<!-- shop.php -->
<script>
// ============================================
// AUTO-APPLY FILTER JAVASCRIPT
// ============================================
let activeFilters = {};
let pendingFilters = {};
let filterDebounceTimer = null; // For price slider debouncing
// Initialize filters from PHP data
const priceRange = {
min: <?php echo $priceRange['min_price']; ?>,
max: <?php echo $priceRange['max_price']; ?>
};
// Auto-apply filters function
function autoApplyFilters() {
activeFilters = JSON.parse(JSON.stringify(pendingFilters));
updateFilterTags();
loadProducts(activeFilters, document.getElementById('sortSelect').value, 1);
updateURLClean();
}
// Load products with current filters
function loadProducts(filters = {}, sort = '', page = 1) {
const loadingIndicator = document.getElementById('loadingIndicator');
const productsContainer = document.getElementById('productsContainer');
loadingIndicator.style.display = 'block';
const formData = new FormData();
formData.append('filters', JSON.stringify(filters));
formData.append('sort', sort);
formData.append('page', page);
fetch('load_products.php', {
method: 'POST',
body: formData
})
.then(response => response.text())
.then(html => {
productsContainer.innerHTML = html;
loadingIndicator.style.display = 'none';
window.goToPage = goToPage;
setTimeout(initOfferTimers, 100);
console.log('Page loaded, checking initial wishlist status...');
setTimeout(() => {
checkAllProductCardWishlistStatus();
}, 500);
setTimeout(() => {
const productsHeader = document.querySelector('.products-header');
if (productsHeader) {
productsHeader.scrollIntoView({ behavior: 'smooth', block: 'start' });
}
}, 100);
})
.catch(error => {
console.error('Error loading products:', error);
loadingIndicator.style.display = 'none';
productsContainer.innerHTML = '<div class="col-12 text-center py-5"><h4>Error loading products</h4><p>Please try again.</p></div>';
});
}
function goToPage(page) {
if (page < 1) return;
const sortValue = document.getElementById('sortSelect').value;
loadProducts(activeFilters, sortValue, page);
}
function updateURLClean() {
const url = new URL(window.location);
const params = Array.from(url.searchParams.keys());
params.forEach(param => url.searchParams.delete(param));
Object.keys(activeFilters).forEach(filterType => {
const filterValue = activeFilters[filterType];
if (Array.isArray(filterValue) && filterValue.length > 0) {
url.searchParams.set(filterType, filterValue.join(','));
} else if (typeof filterValue === 'string' && filterValue) {
url.searchParams.set(filterType, filterValue);
}
});
const sortValue = document.getElementById('sortSelect').value;
if (sortValue && sortValue !== '' && sortValue !== 'name_asc') {
url.searchParams.set('sort', sortValue);
}
window.history.replaceState({}, '', url);
}
function updateFilterTags() {
const container = document.getElementById('filterTags');
const tagsWrapper = container.querySelector('.d-flex');
tagsWrapper.innerHTML = '';
let hasFilters = false;
const hasAnyFilters = Object.keys(activeFilters).some(key =>
activeFilters[key] && (Array.isArray(activeFilters[key]) ? activeFilters[key].length > 0 : true)
);
if (hasAnyFilters) {
const clearAllTag = document.createElement('span');
clearAllTag.className = 'filter-tag clear-all-tag btn-effect';
clearAllTag.innerHTML = 'Clear All';
clearAllTag.addEventListener('click', clearAllFilters);
tagsWrapper.appendChild(clearAllTag);
hasFilters = true;
}
Object.keys(activeFilters).forEach(filterType => {
const filterValue = activeFilters[filterType];
if (!filterValue) return;
if (filterType === 'search') {
const tag = createFilterTag(`Search: "${filterValue}"`, () => removeFilter('search'));
tagsWrapper.appendChild(tag);
hasFilters = true;
} else if (filterType === 'offers') {
const tag = createFilterTag('Offer Products', () => removeFilter('offers'));
tagsWrapper.appendChild(tag);
hasFilters = true;
} else if (filterType === 'product_condition') {
if (Array.isArray(filterValue)) {
filterValue.forEach(value => {
const displayText = value === 'brand_new' ? 'Brand New' : 'Pre-loved';
const tag = createFilterTag(displayText, () => removeFilter(filterType, value));
tagsWrapper.appendChild(tag);
hasFilters = true;
});
}
} else if (Array.isArray(filterValue)) {
filterValue.forEach(value => {
const tag = createFilterTag(value, () => removeFilter(filterType, value));
tagsWrapper.appendChild(tag);
hasFilters = true;
});
} else if (filterType === 'price') {
const tag = createFilterTag(filterValue, () => removeFilter(filterType));
tagsWrapper.appendChild(tag);
hasFilters = true;
} else {
const tag = createFilterTag(filterValue, () => removeFilter(filterType));
tagsWrapper.appendChild(tag);
hasFilters = true;
}
});
container.style.display = hasFilters ? 'block' : 'none';
}
function createFilterTag(text, removeCallback) {
const tag = document.createElement('span');
tag.className = 'filter-tag filter-item btn-effect-light2';
tag.innerHTML = `${text}<button class="remove-tag" type="button">×</button>`;
tag.querySelector('.remove-tag').addEventListener('click', removeCallback);
return tag;
}
function removeFilter(type, value = null) {
if (value === null) {
delete activeFilters[type];
delete pendingFilters[type];
} else {
if (activeFilters[type] && Array.isArray(activeFilters[type])) {
activeFilters[type] = activeFilters[type].filter(item => item !== value);
if (activeFilters[type].length === 0) delete activeFilters[type];
}
if (pendingFilters[type] && Array.isArray(pendingFilters[type])) {
pendingFilters[type] = pendingFilters[type].filter(item => item !== value);
if (pendingFilters[type].length === 0) delete pendingFilters[type];
}
}
updateUIElements();
updateFilterTags();
loadProducts(activeFilters, document.getElementById('sortSelect').value, 1);
updateURLClean();
}
function clearAllFilters() {
activeFilters = {};
pendingFilters = {};
updateUIElements();
const priceSliderMin = document.getElementById('priceSliderMin');
const priceSliderMax = document.getElementById('priceSliderMax');
if (priceSliderMin && priceSliderMax) {
priceSliderMin.value = priceRange.min;
priceSliderMax.value = priceRange.max;
updatePriceDisplay(priceRange.min, priceRange.max);
const priceSliderRange = document.querySelector('.price-slider-range');
if (priceSliderRange) {
priceSliderRange.style.left = '0%';
priceSliderRange.style.width = '100%';
}
delete pendingFilters.price;
}
updateFilterTags();
loadProducts({}, document.getElementById('sortSelect').value, 1);
const url = new URL(window.location);
const params = Array.from(url.searchParams.keys());
params.forEach(param => url.searchParams.delete(param));
window.history.replaceState({}, '', url);
document.getElementById('sortSelect').value = 'name_asc';
}
function updateUIElements() {
document.querySelectorAll('input[type="checkbox"]').forEach(cb => cb.checked = false);
document.querySelectorAll('.size-btn').forEach(btn => btn.classList.remove('active'));
const preLovedToggle = document.getElementById('preLovedToggle');
const brandNewToggle = document.getElementById('brandNewToggle');
if (preLovedToggle) preLovedToggle.checked = false;
if (brandNewToggle) brandNewToggle.checked = false;
const priceSliderMin = document.getElementById('priceSliderMin');
const priceSliderMax = document.getElementById('priceSliderMax');
if (priceSliderMin && priceSliderMax) {
priceSliderMin.value = priceRange.min;
priceSliderMax.value = priceRange.max;
updatePriceDisplay(priceRange.min, priceRange.max);
const priceSliderRange = document.querySelector('.price-slider-range');
if (priceSliderRange) {
priceSliderRange.style.left = '0%';
priceSliderRange.style.width = '100%';
}
}
Object.keys(activeFilters).forEach(filterType => {
const filterValue = activeFilters[filterType];
if (!filterValue) return;
if (Array.isArray(filterValue)) {
filterValue.forEach(value => {
const checkbox = document.querySelector(`input[data-filter="${filterType}"][data-value="${value}"]`);
if (checkbox) checkbox.checked = true;
const sizeBtn = document.querySelector(`.size-btn[data-filter="${filterType}"][data-value="${value}"]`);
if (sizeBtn) sizeBtn.classList.add('active');
});
} else if (filterType === 'price') {
const matches = filterValue.match(/₹(\d+)-₹(\d+)/);
if (matches && priceSliderMin && priceSliderMax) {
const minPrice = parseInt(matches[1]);
const maxPrice = parseInt(matches[2]);
priceSliderMin.value = minPrice;
priceSliderMax.value = maxPrice;
updatePriceDisplay(minPrice, maxPrice);
const minPercent = (minPrice - priceRange.min) / (priceRange.max - priceRange.min) * 100;
const maxPercent = (maxPrice - priceRange.min) / (priceRange.max - priceRange.min) * 100;
const priceSliderRange = document.querySelector('.price-slider-range');
if (priceSliderRange) {
priceSliderRange.style.left = minPercent + '%';
priceSliderRange.style.width = (maxPercent - minPercent) + '%';
}
}
}
});
if (activeFilters.product_condition && Array.isArray(activeFilters.product_condition)) {
activeFilters.product_condition.forEach(condition => {
if (condition === 'pre_loved' && preLovedToggle) preLovedToggle.checked = true;
if (condition === 'brand_new' && brandNewToggle) brandNewToggle.checked = true;
});
}
}
function updatePriceDisplay(minValue, maxValue) {
document.querySelector('.price-from').value = `₹${parseInt(minValue).toLocaleString()}`;
document.querySelector('.price-to').value = `₹${parseInt(maxValue).toLocaleString()}`;
}
// ============================================
// DOM CONTENT LOADED - INITIALIZE EVERYTHING
// ============================================
document.addEventListener('DOMContentLoaded', function() {
const urlParams = new URLSearchParams(window.location.search);
const initialFilters = {};
if (urlParams.get('search')) initialFilters.search = urlParams.get('search');
if (urlParams.get('category')) initialFilters.category = [urlParams.get('category')];
if (urlParams.get('tag')) initialFilters.tag = urlParams.get('tag');
if (urlParams.get('brand')) {
const brandParam = urlParams.get('brand');
initialFilters.brand = brandParam.includes(',') ? brandParam.split(',') : [brandParam];
}
if (urlParams.get('collection')) initialFilters.collection = urlParams.get('collection');
if (urlParams.get('offers')) initialFilters.offers = urlParams.get('offers');
// Product Condition Toggles
const preLovedToggle = document.getElementById('preLovedToggle');
const brandNewToggle = document.getElementById('brandNewToggle');
if (preLovedToggle && brandNewToggle) {
preLovedToggle.addEventListener('change', function() {
if (this.checked && brandNewToggle.checked) {
brandNewToggle.checked = false;
}
if (!pendingFilters.product_condition) {
pendingFilters.product_condition = [];
}
if (this.checked) {
pendingFilters.product_condition = ['pre_loved'];
} else {
pendingFilters.product_condition = pendingFilters.product_condition.filter(v => v !== 'pre_loved');
}
if (pendingFilters.product_condition.length === 0) {
delete pendingFilters.product_condition;
}
autoApplyFilters(); // ✅ Auto-apply
});
brandNewToggle.addEventListener('change', function() {
if (this.checked && preLovedToggle.checked) {
preLovedToggle.checked = false;
}
if (!pendingFilters.product_condition) {
pendingFilters.product_condition = [];
}
if (this.checked) {
pendingFilters.product_condition = ['brand_new'];
} else {
pendingFilters.product_condition = pendingFilters.product_condition.filter(v => v !== 'brand_new');
}
if (pendingFilters.product_condition.length === 0) {
delete pendingFilters.product_condition;
}
autoApplyFilters(); // ✅ Auto-apply
});
}
if (Object.keys(initialFilters).length > 0) {
activeFilters = {...initialFilters};
pendingFilters = {...initialFilters};
Object.keys(initialFilters).forEach(filterType => {
const filterValue = initialFilters[filterType];
if (Array.isArray(filterValue)) {
filterValue.forEach(value => {
const checkbox = document.querySelector(`input[data-filter="${filterType}"][data-value="${value}"]`);
if (checkbox) checkbox.checked = true;
const sizeBtn = document.querySelector(`.size-btn[data-filter="${filterType}"][data-value="${value}"]`);
if (sizeBtn) sizeBtn.classList.add('active');
});
}
});
updateFilterTags();
}
const sortParam = urlParams.get('sort');
if (sortParam) document.getElementById('sortSelect').value = sortParam;
loadProducts(activeFilters, sortParam || '', 1);
// ============================================
// AUTO-APPLY: Filter checkboxes - apply immediately on change
// ============================================
document.querySelectorAll('.filter-checkbox').forEach(checkbox => {
checkbox.addEventListener('change', function() {
const filterType = this.dataset.filter;
const value = this.dataset.value;
if (!pendingFilters[filterType]) pendingFilters[filterType] = [];
if (this.checked) {
if (!pendingFilters[filterType].includes(value)) {
pendingFilters[filterType].push(value);
}
} else {
pendingFilters[filterType] = pendingFilters[filterType].filter(v => v !== value);
if (pendingFilters[filterType].length === 0) delete pendingFilters[filterType];
}
autoApplyFilters(); // ✅ Auto-apply immediately
});
});
// Size buttons - apply immediately
document.querySelectorAll('.size-btn').forEach(btn => {
btn.addEventListener('click', function(e) {
e.preventDefault();
this.classList.toggle('active');
const filterType = this.dataset.filter;
const value = this.dataset.value;
if (!pendingFilters[filterType]) pendingFilters[filterType] = [];
if (this.classList.contains('active')) {
if (!pendingFilters[filterType].includes(value)) {
pendingFilters[filterType].push(value);
}
} else {
pendingFilters[filterType] = pendingFilters[filterType].filter(v => v !== value);
if (pendingFilters[filterType].length === 0) delete pendingFilters[filterType];
}
autoApplyFilters(); // ✅ Auto-apply immediately
});
});
// Price sliders - debounced auto-apply
const priceSliderMin = document.getElementById('priceSliderMin');
const priceSliderMax = document.getElementById('priceSliderMax');
const priceSliderRange = document.querySelector('.price-slider-range');
function updatePriceSliders() {
const minVal = parseInt(priceSliderMin.value);
const maxVal = parseInt(priceSliderMax.value);
if (minVal >= maxVal) {
if (this === priceSliderMin) {
priceSliderMax.value = minVal + 1;
} else {
priceSliderMin.value = maxVal - 1;
}
}
const minPercent = (priceSliderMin.value - priceSliderMin.min) / (priceSliderMin.max - priceSliderMin.min) * 100;
const maxPercent = (priceSliderMax.value - priceSliderMax.min) / (priceSliderMax.max - priceSliderMax.min) * 100;
priceSliderRange.style.left = minPercent + '%';
priceSliderRange.style.width = (maxPercent - minPercent) + '%';
updatePriceDisplay(priceSliderMin.value, priceSliderMax.value);
const minPrice = parseInt(priceSliderMin.value);
const maxPrice = parseInt(priceSliderMax.value);
if (minPrice > priceRange.min || maxPrice < priceRange.max) {
pendingFilters.price = `₹${minPrice}-₹${maxPrice}`;
} else {
delete pendingFilters.price;
}
// ✅ Debounced auto-apply for price slider (wait 500ms after user stops sliding)
clearTimeout(filterDebounceTimer);
filterDebounceTimer = setTimeout(() => {
autoApplyFilters();
}, 500);
}
if (priceSliderMin && priceSliderMax) {
priceSliderMin.addEventListener('input', updatePriceSliders);
priceSliderMax.addEventListener('input', updatePriceSliders);
const minPercent = 0;
const maxPercent = 100;
priceSliderRange.style.left = minPercent + '%';
priceSliderRange.style.width = (maxPercent - minPercent) + '%';
updatePriceDisplay(priceRange.min, priceRange.max);
}
// Reset links
document.querySelectorAll('.reset-link').forEach(link => {
link.addEventListener('click', function() {
const filterType = this.dataset.filter;
delete pendingFilters[filterType];
delete activeFilters[filterType];
document.querySelectorAll(`input[data-filter="${filterType}"]`).forEach(input => {
input.checked = false;
});
document.querySelectorAll(`.size-btn[data-filter="${filterType}"]`).forEach(btn => {
btn.classList.remove('active');
});
if (filterType === 'price' && priceSliderMin && priceSliderMax) {
priceSliderMin.value = priceRange.min;
priceSliderMax.value = priceRange.max;
const priceSliderRange = document.querySelector('.price-slider-range');
if (priceSliderRange) {
priceSliderRange.style.left = '0%';
priceSliderRange.style.width = '100%';
}
updatePriceDisplay(priceRange.min, priceRange.max);
delete pendingFilters.price;
delete activeFilters.price;
}
updateFilterTags();
loadProducts(activeFilters, document.getElementById('sortSelect').value, 1);
updateURLClean();
});
});
// Clear all button
document.getElementById('clearAllFilters').addEventListener('click', clearAllFilters);
// Sort select
document.getElementById('sortSelect').addEventListener('change', function() {
loadProducts(activeFilters, this.value, 1);
updateURLClean();
});
// Offcanvas overlay
const filterOffcanvas = document.getElementById('filterOffcanvas');
const overlay = document.querySelector('.products-grid__overlay');
if (filterOffcanvas && overlay) {
filterOffcanvas.addEventListener('show.bs.offcanvas', () => {
overlay.classList.add('show');
document.body.classList.add('offcanvas-open');
});
filterOffcanvas.addEventListener('hide.bs.offcanvas', () => {
overlay.classList.remove('show');
document.body.classList.remove('offcanvas-open');
});
filterOffcanvas.addEventListener('hidden.bs.offcanvas', () => {
document.body.style.overflow = '';
document.body.style.paddingRight = '';
document.body.classList.remove('modal-open');
});
overlay.addEventListener('click', function() {
const offcanvasInstance = bootstrap.Offcanvas.getInstance(filterOffcanvas);
if (offcanvasInstance) {
offcanvasInstance.hide();
}
});
}
checkAllProductCardWishlistStatus();
});
// Make functions globally available
window.clearAllFilters = clearAllFilters;
window.removeFilter = removeFilter;
window.goToPage = goToPage;
</script>
<script>
// Offer Timer Countdown
function initOfferTimers() {
const timers = document.querySelectorAll('.offer-timer');
if (!timers || timers.length === 0) {
console.log('No offer timers found on page');
return;
}
timers.forEach(timer => {
const timerText = timer.querySelector('.timer-text');
if (!timerText) {
console.warn('Timer text element not found:', timer);
return;
}
const endTime = new Date(timer.dataset.offerEnd).getTime();
function updateTimer() {
const now = Date.now();
const distance = endTime - now;
if (distance < 0) {
if (timerText) timerText.textContent = 'Expired';
timer.style.color = '#999';
return;
}
const days = Math.floor(distance / (1000 * 60 * 60 * 24));
const hours = Math.floor((distance % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
const minutes = Math.floor((distance % (1000 * 60 * 60)) / (1000 * 60));
if (days > 0) {
if (timerText) timerText.textContent = `${days}d ${hours}h`;
} else if (hours > 0) {
if (timerText) timerText.textContent = `${hours}h ${minutes}m`;
} else {
if (timerText) timerText.textContent = `${minutes}m`;
timer.classList.add('expiring-soon');
}
}
updateTimer();
setInterval(updateTimer, 60000);
});
}
window.addEventListener('load', function() {
initOfferTimers();
});
</script>
<script>
// ============================================
// VARIANT SELECTION MODAL SYSTEM - COMPLETE FIX
// ============================================
let modalData = {
pid: null,
actionType: null,
currentVariant: null,
allVariations: [],
optionGroups: {},
selectedOptions: {},
offerData: null,
productImages: []
};
const IMAGE_BASE_PATH = '../Images/Product/';
const variantModal = new bootstrap.Modal(document.getElementById('variantSelectionModal'));
// ============================================
// OPEN MODAL
// ============================================
function openVariantModal(pid, actionType) {
modalData.pid = pid;
modalData.actionType = actionType;
if (event) {
event.stopPropagation();
event.preventDefault();
}
// Reset button states
const wishlistBtn = document.getElementById('modalAddToWishlistBtn');
if (wishlistBtn) {
wishlistBtn.classList.remove('wishlist-active');
wishlistBtn.style.background = '';
wishlistBtn.style.color = '';
wishlistBtn.style.borderColor = '';
wishlistBtn.innerHTML = '<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z"/></svg> Add to Wishlist';
}
document.getElementById('modalProductName').textContent = 'Loading...';
document.getElementById('modalMainImage').src = '';
fetch('get_product_variations.php', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({ pid: pid })
})
.then(r => r.json())
.then(data => {
if (data.success) {
populateModal(data);
variantModal.show();
} else {
alert('Error loading product: ' + data.message);
}
})
.catch(err => {
console.error('Modal error:', err);
alert('Failed to load product details');
});
}
// ============================================
// POPULATE MODAL
// ============================================
// ============================================
// POPULATE MODAL - WITHOUT IMAGE COLUMN
// ============================================
function populateModal(data) {
modalData.allVariations = data.variations;
modalData.optionGroups = data.optionGroups;
modalData.offerData = data.offerData;
modalData.productImages = data.product.images;
console.log('═══════════════════════════════════════');
console.log('📦 VARIANT MODAL - OFFER CHECK');
console.log('Has Offer:', data.hasOffer);
console.log('Offer Data:', data.offerData);
console.log('═══════════════════════════════════════');
// ... existing image setup code ...
document.getElementById('modalProductCategory').textContent = data.product.category || 'Product';
document.getElementById('modalProductName').textContent = data.product.pname;
// ✅ These IDs are now unique to variant modal - will work!
const offerBadge = document.getElementById('modalOfferBadge');
const offerPriceSpan = document.getElementById('modalOfferPrice');
if (data.hasOffer && data.offerData && data.offerData.offer_amount) {
console.log('✅ VARIANT MODAL: Showing offer');
if (offerBadge) {
offerBadge.style.display = 'block';
}
if (offerPriceSpan) {
offerPriceSpan.textContent = '₹' + parseFloat(data.offerData.offer_amount).toFixed(2);
}
} else {
console.log('❌ VARIANT MODAL: No offer');
if (offerBadge) {
offerBadge.style.display = 'none';
}
}
// Set button visibility
const cartBtn = document.getElementById('modalAddToCartBtn');
const wishlistBtn = document.getElementById('modalAddToWishlistBtn');
const quantitySection = document.querySelector('.modal-quantity-section');
if (modalData.actionType === 'cart') {
cartBtn.style.display = 'block';
wishlistBtn.style.display = 'none';
quantitySection.style.display = 'block';
} else if (modalData.actionType === 'wishlist') {
cartBtn.style.display = 'none';
wishlistBtn.style.display = 'block';
quantitySection.style.display = 'none';
checkModalWishlistStatus();
}
buildVariantSelectors(data.optionGroups);
selectDefaultVariant();
bindModalActions();
}
// ✅ NEW: Check wishlist status when modal opens
function checkModalWishlistStatus() {
if (!modalData.currentVariant) {
setTimeout(checkModalWishlistStatus, 100);
return;
}
const checkBody = new URLSearchParams({
pid: String(modalData.pid),
vname: modalData.currentVariant.variant_title,
attributes: JSON.stringify(modalData.selectedOptions),
action: 'check_status'
});
fetch('add_to_wishlist.php', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: checkBody.toString()
})
.then(r => r.json())
.then(data => {
const wishlistBtn = document.getElementById('modalAddToWishlistBtn');
if (wishlistBtn && data.success && data.in_wishlist) {
wishlistBtn.innerHTML = '<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor" stroke="currentColor" stroke-width="2"><path d="M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z"/></svg> In Wishlist';
wishlistBtn.classList.add('wishlist-active');
wishlistBtn.style.background = '#28a745';
wishlistBtn.style.color = 'white';
wishlistBtn.style.borderColor = '#28a745';
wishlistBtn.disabled = false; // ✅ ADD THIS LINE - Keep button enabled
}
})
.catch(err => console.error('Wishlist check error:', err));
}
// ============================================
// BUILD VARIANT SELECTORS
// ============================================
function buildVariantSelectors(optionGroups) {
const container = document.getElementById('variantSelectorsContainer');
container.innerHTML = '';
Object.keys(optionGroups).forEach(optionName => {
const group = document.createElement('div');
group.className = 'variant-selector-group';
const label = document.createElement('label');
label.className = 'variant-selector-label';
label.textContent = optionName.charAt(0).toUpperCase() + optionName.slice(1);
group.appendChild(label);
if (optionName.toLowerCase() === 'color' || optionName.toLowerCase() === 'colour') {
const colorContainer = document.createElement('div');
colorContainer.className = 'color-selector';
optionGroups[optionName].forEach((option, index) => {
const colorDiv = document.createElement('div');
colorDiv.className = 'color_option' + (index === 0 ? ' selected' : '');
colorDiv.dataset.optionName = optionName;
colorDiv.dataset.value = option.value;
colorDiv.title = option.value;
const colorValue = option.value.toLowerCase();
colorDiv.style.setProperty('--color-value', getColorHex(colorValue));
colorDiv.onclick = () => selectOption(optionName, option.value, colorDiv);
colorContainer.appendChild(colorDiv);
});
group.appendChild(colorContainer);
} else {
const sizeContainer = document.createElement('div');
sizeContainer.className = 'size-selector';
optionGroups[optionName].forEach((option, index) => {
const sizeBtn = document.createElement('button');
sizeBtn.type = 'button';
sizeBtn.className = 'size-option' + (index === 0 ? ' selected' : '');
sizeBtn.textContent = option.value;
sizeBtn.dataset.optionName = optionName;
sizeBtn.dataset.value = option.value;
sizeBtn.onclick = () => selectOption(optionName, option.value, sizeBtn);
sizeContainer.appendChild(sizeBtn);
});
group.appendChild(sizeContainer);
}
container.appendChild(group);
});
}
function getColorHex(colorName) {
const colorMap = {
'red': '#dc3545', 'blue': '#0d6efd', 'green': '#198754',
'yellow': '#ffc107', 'orange': '#fd7e14', 'purple': '#6f42c1',
'pink': '#d63384', 'black': '#000000', 'white': '#ffffff',
'gray': '#6c757d', 'grey': '#6c757d', 'brown': '#795548',
'navy': '#000080', 'teal': '#20c997', 'beige': '#F5F5DC'
};
return colorMap[colorName] || colorName;
}
// ============================================
// HANDLE OPTION SELECTION
// ============================================
// ============================================
// HANDLE OPTION SELECTION - WITH DEBUG
// ============================================
function selectOption(optionName, value, element) {
console.log('═══════════════════════════════════════');
console.log('🎨 OPTION CLICKED');
console.log('Option Name:', optionName);
console.log('Option Value:', value);
console.log('Element:', element);
const container = element.parentElement;
console.log('Container:', container);
container.querySelectorAll('.color_option, .size-option').forEach(el => {
el.classList.remove('selected');
});
element.classList.add('selected');
// Store the selection
const normalizedOptionName = optionName.toLowerCase() === 'colour' ? 'color' : optionName;
modalData.selectedOptions[normalizedOptionName] = value;
console.log('📋 Updated selectedOptions:', JSON.stringify(modalData.selectedOptions));
console.log('📋 Current variant BEFORE update:', modalData.currentVariant?.variant_title);
// Check all variations to see which ones match
console.log('🔍 Checking all variations:');
modalData.allVariations.forEach((v, index) => {
console.log(` [${index}] ${v.variant_title}`);
console.log(` - image_path: ${v.image_path || 'NONE'}`);
console.log(` - display_image: ${v.display_image || 'NONE'}`);
console.log(` - variant_data: ${v.variant_data || 'NONE'}`);
if (v.variant_data) {
try {
const vData = JSON.parse(v.variant_data);
console.log(` - parsed data:`, vData);
} catch (e) {
console.log(` - ERROR parsing variant_data`);
}
}
});
console.log('🔄 Calling updateSelectedVariant...');
updateSelectedVariant();
console.log('📋 Current variant AFTER update:', modalData.currentVariant?.variant_title);
console.log('🖼️ Image that should show:', modalData.currentVariant?.image_path || modalData.currentVariant?.display_image);
console.log('═══════════════════════════════════════');
// Re-check wishlist status after variant change
if (modalData.actionType === 'wishlist') {
checkModalWishlistStatus();
}
}
// ============================================
// AUTO-SELECT DEFAULT VARIANT
// ============================================
function selectDefaultVariant() {
if (modalData.allVariations.length === 0) return;
const cheapest = modalData.allVariations
.filter(v => v.quantity > 0)
.sort((a, b) => a.price - b.price)[0] || modalData.allVariations[0];
if (cheapest.variant_data) {
try {
const variantData = JSON.parse(cheapest.variant_data);
Object.keys(variantData).forEach(key => {
if (modalData.optionGroups[key]) {
modalData.selectedOptions[key] = variantData[key];
const selector = document.querySelector(`[data-option-name="${key}"][data-value="${variantData[key]}"]`);
if (selector) {
selector.classList.add('selected');
selector.parentElement.querySelectorAll('.color_option, .size-option').forEach(el => {
if (el !== selector) el.classList.remove('selected');
});
}
}
});
} catch (e) {
console.error('Error parsing variant data:', e);
}
}
modalData.currentVariant = cheapest;
updateModalDisplay();
}
// ============================================
// UPDATE SELECTED VARIANT
// ============================================
// ============================================
// UPDATE SELECTED VARIANT - WITH DEBUG
// ============================================
function updateSelectedVariant() {
console.log('🔍 ========== UPDATE SELECTED VARIANT ==========');
console.log('Looking for variant with options:', modalData.selectedOptions);
const matchingVariant = modalData.allVariations.find(v => {
if (!v.variant_data) {
console.log(`❌ ${v.variant_title} - NO variant_data`);
return false;
}
try {
const variantData = JSON.parse(v.variant_data);
console.log(`🔎 Checking: ${v.variant_title}`);
console.log(` Variant data:`, variantData);
// Check if all selected options match this variant
let allMatch = true;
for (let key in modalData.selectedOptions) {
const selectedValue = modalData.selectedOptions[key].toLowerCase();
let foundMatch = false;
// Check if this key exists in variant data
for (let vKey in variantData) {
if (vKey.toLowerCase() === key.toLowerCase()) {
const variantValue = variantData[vKey].toLowerCase();
console.log(` Comparing ${key}: "${selectedValue}" === "${variantValue}"?`, selectedValue === variantValue);
if (selectedValue === variantValue) {
foundMatch = true;
}
}
}
if (!foundMatch) {
console.log(` ❌ No match for ${key}=${selectedValue}`);
allMatch = false;
break;
}
}
if (allMatch) {
console.log(` ✅ FULL MATCH FOUND!`);
return true;
}
return false;
} catch (e) {
console.error('Error parsing variant data:', e);
return false;
}
});
if (matchingVariant) {
console.log('✅ ========== MATCH RESULT ==========');
console.log('Selected variant:', matchingVariant.variant_title);
console.log('Image path:', matchingVariant.image_path);
console.log('Display image:', matchingVariant.display_image);
console.log('====================================');
modalData.currentVariant = matchingVariant;
updateModalDisplay();
} else {
console.warn('⚠️ NO MATCHING VARIANT FOUND!');
console.log('Selected options were:', modalData.selectedOptions);
}
console.log('🔍 ========================================');
}
// ============================================
// UPDATE MODAL DISPLAY
// ============================================
// ============================================
// UPDATE MODAL DISPLAY
// ============================================
// ============================================
// UPDATE MODAL DISPLAY
// ============================================
function updateModalDisplay() {
if (!modalData.currentVariant) {
console.error('❌ No current variant');
return;
}
const variant = modalData.currentVariant;
console.log('═══════════════════════════════════════');
console.log('💰 UPDATE PRICE DISPLAY');
console.log('Variant:', variant.variant_title);
console.log('Variant Price:', variant.price);
console.log('Has Offer Data:', !!modalData.offerData);
if (modalData.offerData) {
console.log('Offer Amount:', modalData.offerData.offer_amount);
}
console.log('═══════════════════════════════════════');
// ✅ CRITICAL FIX: Calculate and display correct price
const priceEl = document.getElementById('modalProductPrice');
let priceHTML = '';
const originalPrice = parseFloat(variant.price);
// Check if offer exists and is valid
if (modalData.offerData && modalData.offerData.offer_amount) {
const offerPrice = parseFloat(modalData.offerData.offer_amount);
priceEl.innerHTML = `
<span class="current-price" style="color: #667eea; font-weight: 700; font-size: 32px;">
₹${offerPrice.toFixed(2)}
</span>
<span class="original-price" style="text-decoration: line-through; color: #999; font-size: 20px; margin-left: 10px;">
₹${originalPrice.toFixed(2)}
</span>
`;
} else {
priceEl.innerHTML = `
<span class="current-price" style="font-size: 32px;">
₹${originalPrice.toFixed(2)}
</span>
`;
}
priceEl.innerHTML = priceHTML;
// Update stock status
const stockEl = document.getElementById('modalStockStatus');
const cartBtn = document.getElementById('modalAddToCartBtn');
const wishlistBtn = document.getElementById('modalAddToWishlistBtn');
const qtyInput = document.getElementById('modalQuantity');
if (variant.quantity > 0) {
stockEl.className = 'modal-stock-status in-stock';
stockEl.innerHTML = `<i class="fas fa-check-circle"></i> ${variant.quantity} items in stock`;
if (modalData.actionType === 'cart') {
qtyInput.value = 1;
qtyInput.max = variant.quantity;
cartBtn.disabled = false;
}
if (modalData.actionType === 'wishlist') {
wishlistBtn.disabled = false;
}
} else {
stockEl.className = 'modal-stock-status out-of-stock';
stockEl.innerHTML = `<i class="fas fa-times-circle"></i> Out of stock`;
if (modalData.actionType === 'cart') {
cartBtn.disabled = true;
qtyInput.value = 0;
qtyInput.max = 0;
}
if (modalData.actionType === 'wishlist') {
wishlistBtn.disabled = false;
}
}
// Update image
const imgElement = document.getElementById('variantModalImage');
const imgWrapper = document.getElementById('variantModalImageWrapper');
const imgDisplay = imgWrapper?.querySelector('.variant-modal-image-display');
if (!imgElement || !imgDisplay) {
console.error('❌ Modal image elements not found');
return;
}
let imageToShow = null;
if (variant.image_path && variant.image_path.trim() !== '' && variant.image_path !== 'null') {
imageToShow = variant.image_path;
} else if (variant.display_image && variant.display_image.trim() !== '' && variant.display_image !== 'null') {
imageToShow = variant.display_image;
}
if (!imageToShow && variant.variant_data) {
try {
const vData = typeof variant.variant_data === 'string' ? JSON.parse(variant.variant_data) : variant.variant_data;
Object.keys(vData).forEach(attrName => {
const attrValue = vData[attrName];
const attrKey = attrName.toLowerCase();
const attrVal = attrValue.toLowerCase();
const matchingVar = modalData.allVariations.find(v => {
if ((v.display_image || v.image_path) && v.variant_data) {
try {
const checkData = typeof v.variant_data === 'string' ? JSON.parse(v.variant_data) : v.variant_data;
for (let key in checkData) {
if (key.toLowerCase() === attrKey && checkData[key].toLowerCase() === attrVal) {
return true;
}
}
} catch (e) {
return false;
}
}
return false;
});
if (matchingVar) {
if (matchingVar.display_image && matchingVar.display_image.trim() !== '') {
imageToShow = matchingVar.display_image;
} else if (matchingVar.image_path && matchingVar.image_path.trim() !== '') {
imageToShow = matchingVar.image_path;
}
}
});
} catch (e) {
console.error('Error checking variant_data:', e);
}
}
if (!imageToShow && modalData.productImages && modalData.productImages.length > 0) {
imageToShow = modalData.productImages[0];
}
if (imageToShow) {
const imgSrc = imageToShow.includes('Images/Product')
? imageToShow
: (IMAGE_BASE_PATH + imageToShow);
const cacheBuster = '?t=' + new Date().getTime();
const fullImgSrc = imgSrc + cacheBuster;
imgDisplay.style.backgroundImage = 'none';
imgElement.src = '';
void imgElement.offsetWidth;
setTimeout(() => {
imgDisplay.style.backgroundImage = `url('${fullImgSrc}')`;
imgElement.src = fullImgSrc;
}, 10);
imgElement.onerror = function() {
console.error('❌ Failed to load:', fullImgSrc);
const altSrc = IMAGE_BASE_PATH + imageToShow + cacheBuster;
imgDisplay.style.backgroundImage = `url('${altSrc}')`;
this.src = altSrc;
};
} else {
console.error('❌ No image available');
imgDisplay.style.backgroundImage = 'none';
imgWrapper.style.background = '#f0f0f0';
imgElement.src = '';
}
}
// ============================================
// PRODUCT CARD WISHLIST HANDLER - NEW
// ============================================
function handleProductCardWishlist(pid, event) {
event.stopPropagation();
event.preventDefault();
const btn = event.currentTarget;
console.log('Wishlist button clicked. Current state:', btn.classList.contains('in-wishlist') ? 'IN WISHLIST' : 'NOT IN WISHLIST');
if (btn.classList.contains('in-wishlist')) {
console.log('Removing from wishlist...');
// ✅ Get stored data from button
const vname = btn.dataset.vname || '';
const attributes = btn.dataset.attributes || '{}';
const originalHTML = btn.innerHTML;
btn.innerHTML = '<svg width="20" height="20" viewBox="0 0 24 24"><circle cx="12" cy="12" r="10" stroke="currentColor" stroke-width="2" fill="none"/><path d="M8 12h8" stroke="currentColor" stroke-width="2"/></svg>';
btn.disabled = true;
const formData = new URLSearchParams({
pid: String(pid),
vname: vname, // ✅ NEW - Include vname
attributes: attributes, // ✅ NEW - Include attributes
action: 'remove'
});
console.log('Sending remove request:', { pid, vname, attributes }); // ✅ Debug log
fetch('add_to_wishlist.php', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: formData.toString()
})
.then(r => r.json())
.then(data => {
console.log('Remove response:', data);
if (data.success) {
// Remove active state
btn.classList.remove('in-wishlist');
btn.style.background = '';
btn.innerHTML = '<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z"/></svg>';
// ✅ Clear stored data
delete btn.dataset.vname;
delete btn.dataset.attributes;
const svg = btn.querySelector('svg');
if (svg) {
svg.style.stroke = 'currentColor';
svg.style.fill = 'none';
}
if (typeof window.updateWishlistDisplay === 'function') {
window.updateWishlistDisplay();
}
} else {
alert(data.message || 'Failed to remove from wishlist');
btn.innerHTML = originalHTML;
}
})
.catch(err => {
console.error('Error:', err);
alert('Error removing from wishlist');
btn.innerHTML = originalHTML;
})
.finally(() => {
btn.disabled = false;
});
return false;
}
// Not in wishlist - Open modal to add
console.log('Opening modal to add to wishlist...');
openVariantModal(pid, 'wishlist');
}
function removeFromWishlist(pid, btn) {
const formData = new URLSearchParams({
pid: String(pid),
action: 'remove'
});
const originalHTML = btn.innerHTML;
btn.innerHTML = '<i class="fas fa-spinner fa-spin"></i>';
btn.disabled = true;
fetch('add_to_wishlist.php', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: formData.toString()
})
.then(r => r.json())
.then(data => {
if (data.success) {
// ✅ Remove active class and reset icon
btn.classList.remove('in-wishlist');
btn.innerHTML = '<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z"/></svg>';
btn.style.background = '';
btn.style.color = '';
if (typeof window.updateWishlistDisplay === 'function') {
window.updateWishlistDisplay();
}
} else {
alert(data.message || 'Failed to remove from wishlist');
btn.innerHTML = originalHTML;
}
})
.catch(err => {
console.error('Error:', err);
alert('Error removing from wishlist');
btn.innerHTML = originalHTML;
})
.finally(() => {
btn.disabled = false;
});
}
// ✅ Check wishlist status for all product cards on page load
function checkAllProductCardWishlistStatus() {
console.log('Checking wishlist status for all products...');
document.querySelectorAll('.btn-wishlist-overlay').forEach(btn => {
const pid = btn.dataset.pid;
if (!pid) return;
const checkBody = new URLSearchParams({
pid: String(pid),
action: 'check_simple' // ✅ NEW - Fixed action
});
fetch('add_to_wishlist.php', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: checkBody.toString()
})
.then(r => {
if (!r.ok) throw new Error('HTTP error');
return r.json();
})
.then(data => {
console.log(`Product ${pid} wishlist status:`, data);
if (data.success && data.in_wishlist) {
// ✅ Store the vname and attributes
btn.dataset.vname = data.vname || '';
btn.dataset.attributes = data.attributes || '{}';
// Add active state
btn.classList.add('in-wishlist');
btn.style.background = '#28a745';
const svg = btn.querySelector('svg');
if (svg) {
svg.style.stroke = '#fff';
svg.style.fill = '#fff';
}
} else {
// Remove active state
btn.classList.remove('in-wishlist');
btn.style.background = '';
delete btn.dataset.vname;
delete btn.dataset.attributes;
const svg = btn.querySelector('svg');
if (svg) {
svg.style.stroke = 'currentColor';
svg.style.fill = 'none';
}
}
})
.catch(err => console.error('Wishlist check error:', err));
});
}
// Make functions global
window.handleProductCardWishlist = handleProductCardWishlist;
window.checkAllProductCardWishlistStatus = checkAllProductCardWishlistStatus;
// ============================================
// BIND MODAL ACTIONS
// ============================================
function bindModalActions() {
const qtyInput = document.getElementById('modalQuantity');
const cartBtn = document.getElementById('modalAddToCartBtn');
document.getElementById('modalQtyDec').onclick = () => {
const val = parseInt(qtyInput.value) || 1;
if (val > 1) {
qtyInput.value = val - 1;
validateQuantity();
}
};
document.getElementById('modalQtyInc').onclick = () => {
const max = parseInt(qtyInput.max) || 999;
const val = parseInt(qtyInput.value) || 1;
if (val < max) {
qtyInput.value = val + 1;
validateQuantity();
}
};
qtyInput.addEventListener('input', validateQuantity);
qtyInput.addEventListener('change', validateQuantity);
function validateQuantity() {
const val = parseInt(qtyInput.value) || 0;
const max = parseInt(qtyInput.max) || 0;
if (val < 1) {
qtyInput.value = 1;
cartBtn.disabled = false;
} else if (val > max) {
qtyInput.value = max;
cartBtn.disabled = true;
cartBtn.title = 'Quantity exceeds available stock';
} else {
cartBtn.disabled = false;
cartBtn.title = '';
}
}
document.getElementById('modalAddToCartBtn').onclick = handleModalAddToCart;
document.getElementById('modalAddToWishlistBtn').onclick = handleModalAddToWishlist;
}
// ============================================
// HANDLE ADD TO CART
// ============================================
// ============================================
// HANDLE ADD TO CART
// ============================================
function handleModalAddToCart() {
if (!modalData.currentVariant) {
alert('Please select product options');
return;
}
const variant = modalData.currentVariant;
const quantity = parseInt(document.getElementById('modalQuantity').value) || 1;
// ✅ Determine the correct image to send (same priority as display logic)
let imageToSend = null;
// Priority 1: variant's own image_path
if (variant.image_path && variant.image_path.trim() !== '' && variant.image_path !== 'null') {
imageToSend = variant.image_path;
}
// Priority 2: variant's display_image
else if (variant.display_image && variant.display_image.trim() !== '' && variant.display_image !== 'null') {
imageToSend = variant.display_image;
}
// Priority 3: first product image
else if (modalData.productImages && modalData.productImages.length > 0) {
imageToSend = modalData.productImages[0];
}
console.log('📦 Adding to cart with image:', imageToSend);
const formData = new URLSearchParams({
pid: modalData.pid,
vname: variant.variant_title,
quantity: quantity,
attributes: JSON.stringify(modalData.selectedOptions),
image: imageToSend || '',
offer_price: modalData.offerData ? modalData.offerData.offer_amount : ''
});
const btn = document.getElementById('modalAddToCartBtn');
const originalText = btn.textContent;
btn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Adding...';
btn.disabled = true;
fetch('add_to_cart.php', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: formData.toString()
})
.then(r => r.json())
.then(data => {
if (data.success) {
if (typeof window.updateCartDisplay === 'function') {
window.updateCartDisplay();
}
btn.innerHTML = '<i class="fas fa-check"></i> Added to Cart!';
btn.style.background = '#28a745';
setTimeout(() => {
variantModal.hide();
btn.textContent = originalText;
btn.style.background = '';
btn.disabled = false;
}, 1500);
} else {
alert('Failed to add to cart: ' + data.message);
btn.textContent = originalText;
btn.disabled = false;
}
})
.catch(err => {
console.error('Cart error:', err);
alert('Error adding to cart');
btn.textContent = originalText;
btn.disabled = false;
});
}
// ============================================
// HANDLE ADD TO WISHLIST - FIXED
// ============================================
// ============================================
// HANDLE ADD TO WISHLIST - FIXED
// ============================================
function handleModalAddToWishlist() {
if (!modalData.currentVariant) {
alert('Please select product options');
return;
}
const variant = modalData.currentVariant;
const btn = document.getElementById('modalAddToWishlistBtn');
// ✅ If already in wishlist, just close modal
if (btn.classList.contains('wishlist-active')) {
variantModal.hide();
return;
}
// ✅ Determine the correct image to send (same priority as display logic)
let imageToSend = null;
// Priority 1: variant's own image_path
if (variant.image_path && variant.image_path.trim() !== '' && variant.image_path !== 'null') {
imageToSend = variant.image_path;
}
// Priority 2: variant's display_image
else if (variant.display_image && variant.display_image.trim() !== '' && variant.display_image !== 'null') {
imageToSend = variant.display_image;
}
// Priority 3: first product image
else if (modalData.productImages && modalData.productImages.length > 0) {
imageToSend = modalData.productImages[0];
}
console.log('❤️ Adding to wishlist with image:', imageToSend);
const formData = new URLSearchParams({
pid: modalData.pid,
vname: variant.variant_title,
attributes: JSON.stringify(modalData.selectedOptions),
image: imageToSend || '',
offer_price: modalData.offerData ? modalData.offerData.offer_amount : '',
action: 'add'
});
const originalHTML = btn.innerHTML;
btn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Adding...';
btn.disabled = true;
fetch('add_to_wishlist.php', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: formData.toString()
})
.then(r => r.json())
.then(data => {
if (data.success) {
// Update wishlist count
if (typeof window.updateWishlistDisplay === 'function') {
window.updateWishlistDisplay();
}
// Update button to active state
btn.innerHTML = '<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor" stroke="currentColor" stroke-width="2"><path d="M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z"/></svg> In Wishlist';
btn.classList.add('wishlist-active');
btn.style.background = '#28a745';
btn.style.color = 'white';
btn.style.borderColor = '#28a745';
// Update the product card button
const productCardBtn = document.querySelector(`.btn-wishlist-overlay[data-pid="${modalData.pid}"]`);
if (productCardBtn) {
productCardBtn.classList.add('in-wishlist');
productCardBtn.style.background = '#28a745';
productCardBtn.querySelector('svg').style.stroke = '#fff';
productCardBtn.querySelector('svg').style.fill = '#fff';
}
setTimeout(() => {
variantModal.hide();
}, 1500);
} else {
alert(data.message || 'Failed to add to wishlist');
btn.innerHTML = originalHTML;
}
})
.catch(err => {
console.error('Wishlist error:', err);
alert('Error adding to wishlist');
btn.innerHTML = originalHTML;
})
.finally(() => {
setTimeout(() => {
btn.disabled = false;
}, 1500);
});
}
window.openVariantModal = openVariantModal;
</script>
</body>
</html>