|
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 ] |
|---|
<!--load_products.php-->
<?php
if (session_status() === PHP_SESSION_NONE) {
session_start();
}
require_once "../config/config.php";
require_once "../config/db.php";
// ... rest of the code
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()));
}
// Get filters and sort from POST request
$filters = isset($_POST['filters']) ? json_decode($_POST['filters'], true) : [];
$sort = isset($_POST['sort']) ? $_POST['sort'] : '';
$currentPage = isset($_POST['page']) ? max(1, (int)$_POST['page']) : 1;
$productsPerPage = 10;
// Helper function to make URL-friendly strings
function makeUrl($string) {
return strtolower(str_replace(' ', '-', preg_replace('/[^A-Za-z0-9 ]/', '', $string)));
}
function getProductGallery($pdo, $pid) {
try {
$sql = "SELECT pimg FROM products WHERE pid = ?";
$stmt = $pdo->prepare($sql);
$stmt->execute([$pid]);
$pimg = $stmt->fetch(PDO::FETCH_COLUMN);
if ($pimg) {
$images = array_map('trim', explode(',', $pimg));
return array_filter($images);
}
return [];
} catch (PDOException $e) {
return [];
}
}
function buildProductQuery($pdo, $filters, $sort) {
$baseQuery = "
SELECT
p.pid AS PID,
p.pname AS PNAME,
p.pthumb AS THUMBNAIL,
p.pimg AS SINGLE_THUMB,
p.pdes AS PDES,
p.category AS CATEGORY,
p.ptag AS PTAG,
p.status AS STATUS,
s.MIN_PRICE,
s.MAX_PRICE,
s.TOTAL_QUANTITY,
lp.variant_title AS VNAME,
s.MIN_PRICE AS NEW_PRICE,
s.MAX_PRICE AS OLD_PRICE,
lp.image_path AS VARIANT_IMAGE,
lp.variant_data AS VARIANT_DATA,
-- Add offer data
o.offer_id AS OFFER_ID,
o.offer_amount AS OFFER_PRICE,
o.offer_valid_until AS OFFER_VALID_UNTIL
FROM products p
LEFT JOIN (
SELECT
pv.PID,
MIN(pv.price) AS MIN_PRICE,
MAX(pv.price) AS MAX_PRICE,
COALESCE(SUM(pv.quantity),0) AS TOTAL_QUANTITY
FROM product_variations pv
GROUP BY pv.PID
) s ON s.PID = p.pid
LEFT JOIN (
SELECT pv.*
FROM product_variations pv
INNER JOIN (
SELECT PID, MIN(price) AS min_price
FROM product_variations
GROUP BY PID
) mm ON mm.PID = pv.PID AND mm.min_price = pv.price
) lp ON lp.PID = p.pid
-- Join with offers table for logged-in users
LEFT JOIN tbl_offers o ON o.product_id = p.pid
AND o.status = 'accepted'
AND o.offer_valid_until > NOW()
" . (isset($_SESSION['user_id']) ? "AND o.user_id = " . (int)$_SESSION['user_id'] : "AND 1=0") . "
";
$whereConditions = [];
$havingConditions = [];
$params = [];
$whereConditions[] = "p.status IN ('approved','published')";
if (!empty($filters)) {
foreach ($filters as $filterType => $filterValues) {
if ($filterValues === null || $filterValues === '' || $filterValues === []) continue;
switch ($filterType) {
case 'offers':
// ✅ NEW: Filter for offer products only
if ($filterValues === 'true' || $filterValues === true) {
if (isset($_SESSION['user_id'])) {
$whereConditions[] = "o.offer_id IS NOT NULL";
} else {
// If not logged in, show nothing
$whereConditions[] = "1 = 0";
}
}
break;
case 'search':
$whereConditions[] = "(
LOWER(p.pname) LIKE LOWER(?)
OR LOWER(p.pdes) LIKE LOWER(?)
OR LOWER(p.category) LIKE LOWER(?)
OR LOWER(p.ptag) LIKE LOWER(?)
)";
$searchTerm = "%{$filterValues}%";
$params[] = $searchTerm;
$params[] = $searchTerm;
$params[] = $searchTerm;
$params[] = $searchTerm;
break;
case 'tag':
$whereConditions[] = "LOWER(p.ptag) LIKE LOWER(?)";
$params[] = "%{$filterValues}%";
break;
case 'collection':
if (!empty($filterValues)) {
try {
// Get collection ID from name
$collectionQuery = "SELECT id FROM collections WHERE collection_name = ? LIMIT 1";
$collectionStmt = $pdo->prepare($collectionQuery);
$collectionStmt->execute([$filterValues]);
$collectionId = $collectionStmt->fetchColumn();
if ($collectionId !== false && $collectionId !== null) {
// Filter products with this exact collection_id
$whereConditions[] = "p.collection_id = ?";
$params[] = (int)$collectionId;
} else {
// Collection name doesn't exist - show nothing
$whereConditions[] = "1 = 0";
}
} catch (PDOException $e) {
error_log("Collection filter error: " . $e->getMessage());
// If error, don't filter by collection
}
}
break;
case 'brand':
if (is_array($filterValues) && $filterValues) {
$placeholders = implode(',', array_fill(0, count($filterValues), '?'));
$whereConditions[] = "p.pid IN (
SELECT DISTINCT PID
FROM product_variation_options
WHERE option_name = 'brand'
AND option_value IN ($placeholders)
)";
$params = array_merge($params, $filterValues);
}
break;
case 'price':
if (preg_match('/₹(\d+)-₹(\d+)/', $filterValues, $m)) {
$minPrice = (int)$m[1];
$maxPrice = (int)$m[2];
$havingConditions[] = "s.MIN_PRICE <= ? AND s.MAX_PRICE >= ?";
$params[] = $maxPrice;
$params[] = $minPrice;
}
break;
case 'availability':
if (is_array($filterValues)) {
$availORs = [];
if (in_array('in_stock', $filterValues, true)) $availORs[] = "s.TOTAL_QUANTITY > 0";
if (in_array('out_of_stock', $filterValues, true)) $availORs[] = "s.TOTAL_QUANTITY <= 0";
if ($availORs) $havingConditions[] = '(' . implode(' OR ', $availORs) . ')';
}
break;
case 'category':
if (is_array($filterValues) && $filterValues) {
$placeholders = implode(',', array_fill(0, count($filterValues), '?'));
$whereConditions[] = "p.category IN ($placeholders)";
$params = array_merge($params, $filterValues);
}
break;
case 'product_condition':
if (is_array($filterValues) && count($filterValues) > 0) {
$placeholders = implode(',', array_fill(0, count($filterValues), '?'));
$whereConditions[] = "p.product_condition IN ($placeholders)";
$params = array_merge($params, $filterValues);
}
break;
default:
if (is_array($filterValues) && $filterValues) {
$placeholders = implode(',', array_fill(0, count($filterValues), '?'));
$whereConditions[] = "p.pid IN (
SELECT DISTINCT PID
FROM product_variation_options
WHERE option_name = ? AND option_value IN ($placeholders)
)";
$params[] = $filterType;
$params = array_merge($params, $filterValues);
}
break;
}
}
}
if ($whereConditions) $baseQuery .= " WHERE " . implode(" AND ", $whereConditions);
$baseQuery .= " GROUP BY p.pid";
if ($havingConditions) $baseQuery .= " HAVING " . implode(" AND ", $havingConditions);
switch ($sort) {
case 'name_asc': $baseQuery .= " ORDER BY p.pname ASC"; break;
case 'name_desc': $baseQuery .= " ORDER BY p.pname DESC"; break;
case 'price_asc': $baseQuery .= " ORDER BY s.MIN_PRICE ASC"; break;
case 'price_desc': $baseQuery .= " ORDER BY s.MAX_PRICE DESC"; break;
case 'newest': $baseQuery .= " ORDER BY p.pid DESC"; break;
default: $baseQuery .= " ORDER BY p.pid DESC"; break;
}
return ['query' => $baseQuery, 'params' => $params];
}
// Updated function calls - now pass $pdo
function getTotalProductCount($pdo, $filters) {
$queryData = buildProductQuery($pdo, $filters, '');
$stmt = $pdo->prepare($queryData['query']);
$stmt->execute($queryData['params']);
$results = $stmt->fetchAll(PDO::FETCH_ASSOC);
return count($results);
}
function getPaginatedResults($pdo, $filters, $sort, $page = 1, $perPage = 3) {
$queryData = buildProductQuery($pdo, $filters, $sort);
$offset = ($page - 1) * $perPage;
$paginatedQuery = $queryData['query'] . " LIMIT $perPage OFFSET $offset";
try {
$stmt = $pdo->prepare($paginatedQuery);
$stmt->execute($queryData['params']);
return $stmt->fetchAll(PDO::FETCH_ASSOC);
} catch (PDOException $e) {
error_log("Pagination query error: " . $e->getMessage());
return [];
}
}
function getProductHoverImage($pdo, $pid, $mainImage) {
try {
$sql = "SELECT pimg FROM products WHERE pid = ?";
$stmt = $pdo->prepare($sql);
$stmt->execute([$pid]);
$pimg = $stmt->fetch(PDO::FETCH_COLUMN);
if ($pimg) {
$galleryImages = array_map('trim', explode(',', $pimg));
$galleryImages = array_filter($galleryImages);
if (!empty($galleryImages)) {
foreach ($galleryImages as $img) {
if ($img != $mainImage) {
return $img;
}
}
}
}
$sql = "SELECT pthumb FROM products WHERE pid = ?";
$stmt = $pdo->prepare($sql);
$stmt->execute([$pid]);
$pthumb = $stmt->fetch(PDO::FETCH_COLUMN);
if ($pthumb && $pthumb != $mainImage) {
return $pthumb;
}
$sql = "SELECT image_path FROM product_variations WHERE PID = ? AND image_path IS NOT NULL AND image_path != '' AND image_path != ? LIMIT 1";
$stmt = $pdo->prepare($sql);
$stmt->execute([$pid, $mainImage]);
$result = $stmt->fetch(PDO::FETCH_COLUMN);
if ($result) {
return $result;
}
return null;
} catch (PDOException $e) {
error_log("Hover image error: " . $e->getMessage());
return null;
}
}
// Get total count and calculate pagination
$totalProducts = getTotalProductCount($pdo, $filters);
$totalPages = ceil($totalProducts / $productsPerPage);
if ($currentPage > $totalPages && $totalPages > 0) {
$currentPage = $totalPages;
}
// Get paginated products
$products = getPaginatedResults($pdo, $filters, $sort, $currentPage, $productsPerPage);
// Set image URL base path
$imageUrl = '../Images/Product';
// ========== DEBUG OUTPUT ==========
echo "<!-- ========== PAGINATION DEBUG ========== -->\n";
echo "<!-- Total Products: " . $totalProducts . " -->\n";
echo "<!-- Products Per Page: " . $productsPerPage . " -->\n";
echo "<!-- Total Pages: " . $totalPages . " -->\n";
echo "<!-- Current Page: " . $currentPage . " -->\n";
echo "<!-- Products Retrieved: " . count($products) . " -->\n";
echo "<!-- Products Empty: " . (empty($products) ? 'YES' : 'NO') . " -->\n";
echo "<!-- Condition \$totalPages > 1: " . (($totalPages > 1) ? 'TRUE' : 'FALSE') . " -->\n";
echo "<!-- ====================================== -->\n\n";
// Start output buffering for products
ob_start();
?>
<?php if(!empty($products)): ?>
<?php foreach($products as $row): ?>
<?php
$displayImage = !empty($row['VARIANT_IMAGE']) ? $row['VARIANT_IMAGE'] :
(!empty($row['THUMBNAIL']) ? $row['THUMBNAIL'] : $row['SINGLE_THUMB']);
$hoverImage = getProductHoverImage($pdo, $row['PID'], $displayImage);
if (empty($hoverImage) && !empty($row['SINGLE_THUMB']) && $row['SINGLE_THUMB'] != $displayImage) {
$hoverImage = $row['SINGLE_THUMB'];
}
$hasOffer = !empty($row['OFFER_ID']) && !empty($row['OFFER_VALID_UNTIL']);
$offerPrice = $hasOffer ? $row['OFFER_PRICE'] : null;
$offerValidUntil = $hasOffer ? $row['OFFER_VALID_UNTIL'] : null;
$displayPrice = $hasOffer ? $offerPrice : $row['NEW_PRICE'];
$originalPrice = $row['OLD_PRICE'];
$discountPercent = 0;
if($originalPrice > $displayPrice) {
$discountPercent = round((($originalPrice - $displayPrice) / $originalPrice) * 100);
}
$productDetailUrl = "product_details.php?product=" . makeUrl($row['PNAME']) . "&variant=" . makeUrl($row['VNAME']) . "&pid=" . $row['PID'];
?>
<div class="col-6 col-xl-3 col-lg-4 col-md-4 col-sm-6 shop-ui-products-count">
<div class="card product-card"
data-product-url="<?php echo $productDetailUrl; ?>"
<?php if(!empty($hoverImage)): ?>data-hover-image="<?php echo $imageUrl; ?>/<?php echo $hoverImage; ?>"<?php endif; ?>
onclick="window.location.href='<?php echo $productDetailUrl; ?>'"
style="cursor: pointer;">
<div class="product-image">
<img src="<?php echo $imageUrl; ?>/<?php echo $displayImage; ?>"
alt="<?php echo htmlspecialchars($row['PNAME']); ?>"
class="main-image">
<?php if(!empty($hoverImage)): ?>
<img src="<?php echo $imageUrl; ?>/<?php echo $hoverImage; ?>"
alt="<?php echo htmlspecialchars($row['PNAME']); ?>"
class="hover-image">
<?php endif; ?>
<!-- Product Badges (Top Left) -->
<?php if($row['TOTAL_QUANTITY'] <= 0): ?>
<div class="product-badge out-of-stock">
<span>Out of Stock</span>
</div>
<?php elseif($hasOffer): ?>
<div class="product-badge offer-badge">
<span>Offer Product</span>
</div>
<?php elseif($discountPercent > 0): ?>
<div class="product-badge discount">
<span><?php echo $discountPercent; ?>% Off</span>
</div>
<?php endif; ?>
<!-- Wishlist Button (Top Right - Hover Only) -->
<!-- Wishlist Button (Top Right - Hover Only) -->
<button class="btn-wishlist-overlay"
onclick="handleProductCardWishlist(<?php echo $row['PID']; ?>, event); return false;"
data-pid="<?php echo $row['PID']; ?>"
title="Add to Wishlist">
<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>
</button>
<!-- Add to Cart Button (Bottom Center - Hover Only) -->
<button class="btn-cart-overlay"
onclick="openVariantModal(<?php echo $row['PID']; ?>, 'cart'); return false;"
<?php echo $row['TOTAL_QUANTITY'] <= 0 ? 'disabled' : ''; ?>>
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<circle cx="9" cy="21" r="1"></circle>
<circle cx="20" cy="21" r="1"></circle>
<path d="M1 1h4l2.68 13.39a2 2 0 0 0 2 1.61h9.72a2 2 0 0 0 2-1.61L23 6H6"></path>
</svg>
Add to Cart
</button>
</div>
<div class="product-info">
<div class="product-brand"><?php echo htmlspecialchars($row['CATEGORY'] ?: 'Product'); ?></div>
<div class="product-title">
<a style="text-decoration:none" href="<?php echo $productDetailUrl; ?>" onclick="event.stopPropagation();">
<?php echo htmlspecialchars($row['PNAME']); ?>
</a>
</div>
<?php if($hasOffer): ?>
<div class="product-price-row">
<div class="product-price">
<span class="current-price offer-price">₹<?php echo number_format($offerPrice, 2); ?></span>
<span class="original-price">₹<?php echo number_format($originalPrice, 2); ?></span>
</div>
<div class="offer-timer" data-offer-end="<?php echo $offerValidUntil; ?>">
<i class="fas fa-clock"></i>
<span class="timer-text"></span>
</div>
</div>
<?php else: ?>
<div class="product-price">
<span class="current-price">₹<?php echo number_format($displayPrice, 2); ?></span>
<?php if($displayPrice != $originalPrice): ?>
<span class="original-price">₹<?php echo number_format($originalPrice, 2); ?></span>
<?php endif; ?>
</div>
<?php endif; ?>
<!-- Keep Existing: Shop Now & Quick View Buttons -->
<div class="product-actions">
<button class="btn-shop"
onclick="event.stopPropagation(); window.location.href='<?php echo $productDetailUrl; ?>'">
Shop Now
</button>
<span class="action-separator">|</span>
<button class="btn-quick-view"
onclick="event.stopPropagation(); openQuickView(<?php echo $row['PID']; ?>)"
data-bs-toggle="modal"
data-bs-target="#productModal"
data-id='<?php echo $row['PID']; ?>'
data-image='<?php echo $displayImage; ?>'
data-pname='<?php echo htmlspecialchars($row['PNAME']); ?>'
data-vname='<?php echo htmlspecialchars($row['VNAME']); ?>'
data-des='<?php echo htmlspecialchars($row['PDES']); ?>'
data-price='₹<?php echo number_format($displayPrice, 2); ?>'
data-old-price='₹<?php echo number_format($originalPrice, 2); ?>'
data-cname='<?php echo htmlspecialchars($row['CATEGORY']); ?>'
data-quantity='<?php echo $row['TOTAL_QUANTITY']; ?>'
data-product-url='<?php echo $productDetailUrl; ?>'>
Quick View
</button>
</div>
</div>
</div>
</div>
<?php endforeach; ?>
<!-- PAGINATION DEBUG BOX -->
<!-- <div class="col-12 mt-3" style="background: #2196F3; color: white; padding: 15px; border-radius: 5px;">
<h4>📄 Pagination Check:</h4>
<p>Will pagination show? <strong><?php echo ($totalPages > 1) ? '✅ YES' : '❌ NO'; ?></strong></p>
<p>Reason: totalPages (<?php echo $totalPages; ?>) <?php echo ($totalPages > 1) ? '> 1' : '<= 1'; ?></p>
</div> -->
<!-- ACTUAL PAGINATION -->
<?php if($totalPages > 1): ?>
<div class="col-12 mt-5 mt-smm-0" style="padding: 20px;">
<nav aria-label="Page navigation">
<ul class="pagination justify-content-center align-items-center gap-2">
<!-- Previous Button -->
<li class="page-item <?php echo $currentPage <= 1 ? 'disabled' : ''; ?>">
<a class="page-link border-0 d-flex align-items-center justify-content-center"
href="#"
onclick="<?php echo $currentPage > 1 ? 'goToPage('.($currentPage - 1).'); return false;' : 'return false;'; ?>"
style="background: transparent;">
<!-- Left Arrow -->
<svg width="35" height="35" viewBox="0 0 45 44" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M16.8189 22.8597H31.5V21.1344H16.8189L23.7285 14.2249L22.5 13L13.5 22L22.5 31L23.7285 29.7751L16.8189 22.8597Z" fill="#000"/>
</svg>
</a>
</li>
<!-- Page Numbers -->
<?php
$startPage = max(1, min($currentPage - 1, $totalPages - 2));
$endPage = min($totalPages, max($currentPage + 1, 3));
for($i = $startPage; $i <= $endPage; $i++): ?>
<li class="page-item">
<a class="page-link d-flex align-items-center justify-content-center <?php echo $i === $currentPage ? 'active' : ''; ?>"
href="#"
onclick="goToPage(<?php echo $i; ?>); return false;"
style="width: 35px; height: 35px; border: none; background: <?php echo $i === $currentPage ? '#000' : 'transparent'; ?>; color: <?php echo $i === $currentPage ? '#fff' : '#000'; ?>; border-radius: 4px; font-weight: 500;font-size: 13px;">
<?php echo $i; ?>
</a>
</li>
<?php endfor; ?>
<!-- Next Button -->
<li class="page-item <?php echo $currentPage >= $totalPages ? 'disabled' : ''; ?>">
<a class="page-link border-0 d-flex align-items-center justify-content-center"
href="#"
onclick="<?php echo $currentPage < $totalPages ? 'goToPage('.($currentPage + 1).'); return false;' : 'return false;'; ?>"
style="background: transparent;">
<!-- Right Arrow -->
<svg width="35" height="35" viewBox="0 0 45 44" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M28.1811 22.8597H13.5V21.1344H28.1811L21.2715 14.2249L22.5 13L31.5 22L22.5 31L21.2715 29.7751L28.1811 22.8597Z" fill="#000"/>
</svg>
</a>
</li>
</ul>
</nav>
</div>
<?php endif; ?>
<?php else: ?>
<div class="col-12 text-center py-5">
<h4>No products found</h4>
<p>Try adjusting your filters or search criteria.</p>
<button class="btn btn-dark mt-3" onclick="clearAllFilters()">Clear All Filters</button>
</div>
<?php endif; ?>
<?php
$output = ob_get_clean();
echo $output;
?>
<style>
/* ============================================ */
/* PRODUCT CARD STYLES - COMPLETE */
/* ============================================ */
/* Product Card Container */
.product-card {
overflow: hidden;
transition: transform 0.3s ease;
border: none;
height: 100%;
box-shadow: none;
position: relative;
}
.product-card:hover {
transform: translateY(-5px);
box-shadow: none;
}
/* Product Image Container */
.product-image {
position: relative;
height: 600px;
overflow: hidden;
}
.product-image .main-image,
.product-image .hover-image {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
object-fit: cover;
transition: opacity 0.3s ease;
border-radius: 10px;
}
.product-image .hover-image {
opacity: 0;
}
.product-card:hover .product-image .main-image {
opacity: 0;
}
.product-card:hover .product-image .hover-image {
opacity: 1;
}
/* ============================================ */
/* HOVER OVERLAY BUTTONS */
/* ============================================ */
/* Wishlist Button (Top Right Corner - Hover Only) */
.btn-wishlist-overlay {
position: absolute;
top: 12px;
right: 12px;
width: 40px;
height: 40px;
background: rgba(255, 255, 255, 0.95);
border: none;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
opacity: 0;
transform: translateY(-10px);
transition: all 0.3s ease;
z-index: 10;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
.btn-wishlist-overlay:hover {
transform: translateY(-10px) scale(1.1);
}
.btn-wishlist-overlay:hover svg {
stroke: #fff;
fill: #fff;
}
.product-card:hover .btn-wishlist-overlay {
opacity: 1;
transform: translateY(0);
}
.btn-wishlist-overlay svg {
stroke: #000;
transition: all 0.3s ease;
}
/* Add to Cart Button (Bottom Center - Hover Only) */
.btn-cart-overlay {
position: absolute;
bottom: 20px;
left: 50%;
transform: translateX(-50%) translateY(20px);
background: #000;
color: white;
border: none;
border-radius: 8px;
padding: 12px 100px;
font-size: 14px;
font-weight: 500;
display: flex;
align-items: center;
gap: 8px;
cursor: pointer;
opacity: 0;
transition: all 0.3s ease;
z-index: 10;
box-shadow: 0 4px 12px rgba(0,0,0,0.2);
white-space: nowrap;
}
.btn-cart-overlay:hover {
background: #333;
transform: translateX(-50%) translateY(20px) scale(1.05);
}
.btn-cart-overlay:disabled {
background: #999;
cursor: not-allowed;
opacity: 0.6;
}
.btn-cart-overlay:disabled:hover {
transform: translateX(-50%) translateY(20px);
}
.product-card:hover .btn-cart-overlay {
opacity: 1;
transform: translateX(-50%) translateY(0);
}
.btn-cart-overlay svg {
width: 18px;
height: 18px;
}
/* ============================================ */
/* PRODUCT BADGES (Top Left) */
/* ============================================ */
.product-badge {
position: absolute;
top: 10px;
left: 10px;
z-index: 2;
}
.product-badge.out-of-stock {
background: #dc3545;
color: white;
padding: 5px 10px;
border-radius: 4px;
font-size: 12px;
}
.product-badge.discount {
background: black;
color: white;
padding: 5px 10px;
border-radius: 4px;
font-size: 12px;
}
/* Offer Badge (Top Left - NOT Top Right) */
.product-badge.offer-badge {
background: black;
color: white;
padding: 6px 12px;
border-radius: 4px;
font-size: 11px;
font-weight: 500;
letter-spacing: 0.5px;
text-transform: uppercase;
}
/* ============================================ */
/* PRODUCT INFO SECTION */
/* ============================================ */
.product-info {
padding: 20px 0;
position: relative;
}
.product-brand {
color: black;
font-size: 14px;
margin-bottom: 5px;
}
.product-title {
font-size: 13px;
margin-bottom: 10px;
color: #666666;
}
.product-title a {
color: #666666;
font-size: 13px;
text-decoration: none;
}
/* ============================================ */
/* PRICE DISPLAY */
/* ============================================ */
.product-price {
font-size: 14px;
color: black;
transition: opacity 0.3s ease;
font-weight: 500;
}
.product-price .current-price {
color: black;
font-weight: 500;
}
.product-price .original-price {
text-decoration: line-through;
color: #999;
margin-left: 8px;
font-size: 13px;
}
.product-price .offer-price {
color: #667eea;
font-weight: 700;
}
/* Price Row (for offer products with timer) */
.product-price-row {
display: flex;
justify-content: space-between;
align-items: center;
gap: 10px;
transition: opacity 0.3s ease;
}
/* Hide price on hover */
.product-card:hover .product-price,
.product-card:hover .product-price-row {
opacity: 0;
}
/* ============================================ */
/* OFFER TIMER */
/* ============================================ */
.offer-timer {
display: flex;
align-items: center;
gap: 4px;
font-size: 11px;
color: #dc3545;
font-weight: 500;
white-space: nowrap;
flex-shrink: 0;
}
.offer-timer i {
font-size: 11px;
}
.offer-timer .timer-text {
font-weight: 600;
}
.offer-timer.expiring-soon {
animation: pulse 1.5s ease-in-out infinite;
}
@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.6; }
}
/* ============================================ */
/* SHOP NOW & QUICK VIEW BUTTONS */
/* (Bottom - Show on Hover) */
/* ============================================ */
.product-actions {
position: absolute;
bottom: 20px;
left: 0;
display: flex;
align-items: center;
opacity: 0;
transition: opacity 0.3s ease;
gap: 0;
}
.product-card:hover .product-actions {
opacity: 1;
}
.btn-shop,
.btn-quick-view {
background: none;
border: none;
color: #666;
font-size: 14px;
text-decoration: none;
margin: 0;
cursor: pointer;
transition: color 0.3s ease;
}
.btn-shop {
padding-right: 8px;
border-right: 1px solid #ccc;
}
.btn-quick-view {
padding-left: 8px;
}
.btn-shop:hover,
.btn-quick-view:hover {
color: #000;
}
.action-separator {
display: none;
}
/* ============================================ */
/* PAGINATION STYLES */
/* ============================================ */
.pagination {
list-style: none;
padding: 0;
margin: 0;
}
.pagination .page-link:hover:not(.active) {
background-color: #f5f5f5 !important;
}
.pagination .page-item.disabled .page-link {
opacity: 0.4;
cursor: not-allowed;
pointer-events: none;
}
/* ============================================ */
/* MOBILE RESPONSIVE */
/* ============================================ */
@media (max-width: 767px) {
.product-card {
border-top: 1px solid #e5e7eb;
border-radius: 0 !important;
}
.product-image {
height: 250px !important;
border-radius: 0 !important;
border: none !important;
}
.product-image .main-image,
.product-image .hover-image {
border-radius: 0 !important;
}
.btn-cart-overlay {
font-size: 12px;
padding: 10px 16px;
bottom: 15px;
}
.btn-wishlist-overlay {
width: 36px;
height: 36px;
top: 10px;
right: 10px;
}
.btn-wishlist-overlay svg {
width: 16px;
height: 16px;
}
.product-info {
padding: 11px 0 !important;
}
.product-title {
margin-bottom: 6px !important;
}
.product-actions {
bottom: 12px !important;
}
.product-actions button {
font-size: 13px !important;
}
}
/* Quantity Input Validation */
.quantity-input:invalid,
.quantity-input.error {
border-color: #dc3545;
}
/* Disabled Add to Cart Button */
#modalAddToCartBtn:disabled {
background: #6c757d !important;
cursor: not-allowed;
opacity: 0.6;
}
#modalAddToCartBtn:disabled:hover {
transform: none;
box-shadow: none;
}
</style>