Files
tomsjavajive/shop.php
T
myron 6e86031576 Fix add-to-cart: bust main.js SW cache, add inline onclick to shop buttons
Service worker was caching main.js (cache-first strategy) so event listeners
may not have been running. Added filemtime version param to main.js like CSS.
Also added inline onclick to shop page buttons so they work regardless of
whether event delegation is functional.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-14 20:11:35 +00:00

210 lines
11 KiB
PHP

<?php
/**
* Tom's Java Jive - Shop Page
*/
$pageTitle = "Shop - Tom's Java Jive";
require_once __DIR__ . '/includes/functions.php';
// Get filters
$category = $_GET['category'] ?? '';
$subcat = $_GET['subcat'] ?? '';
$search = $_GET['search'] ?? '';
$sort = $_GET['sort'] ?? 'newest';
$page = max(1, intval($_GET['page'] ?? 1));
// Build query
$where = ['is_active = 1'];
$params = [];
if ($category) {
$where[] = 'category = :category';
$params['category'] = $category;
}
if ($subcat) {
$where[] = 'product_type_id = :subcat';
$params['subcat'] = $subcat;
}
if ($search) {
$where[] = '(name LIKE :search OR description LIKE :search)';
$params['search'] = '%' . $search . '%';
}
$whereClause = implode(' AND ', $where);
// Prefix columns that are ambiguous in the JOIN query
$joinWhereClause = str_replace(
['is_active = 1', 'category =', 'product_type_id =', '(name LIKE', 'description LIKE'],
['p.is_active = 1', 'p.category =', 'p.product_type_id =', '(p.name LIKE', 'p.description LIKE'],
$whereClause
);
// Sort
$orderBy = match($sort) {
'price_low' => 'COALESCE(p.sale_price, p.price) ASC',
'price_high' => 'COALESCE(p.sale_price, p.price) DESC',
'name' => 'p.name ASC',
default => 'p.created_at DESC'
};
// Get total count
$totalProducts = db()->count('products', $whereClause, $params);
$pagination = paginate($totalProducts, $page, 12);
// Get products
$products = db()->fetchAll(
"SELECT p.*, pt.name AS type_name, pt.type_id AS type_slug FROM products p LEFT JOIN product_types pt ON p.product_type_id = pt.type_id WHERE {$joinWhereClause} ORDER BY {$orderBy} LIMIT :limit OFFSET :offset",
array_merge($params, ['limit' => $pagination['per_page'], 'offset' => $pagination['offset']])
);
// Get categories for filter
$categories = db()->fetchAll(
"SELECT DISTINCT category FROM products WHERE category IS NOT NULL AND category != '' AND is_active = 1 ORDER BY category"
);
$metaTitle = "Shop Premium Coffee Beans | Tom's Java Jive";
$metaDescription = 'Browse our selection of premium artisan coffee beans. Single origin, blends, light, medium and dark roasts. Free shipping over $50.';
$metaKeywords = 'buy coffee beans online, artisan coffee, single origin, blends, light roast, dark roast';
$canonicalUrl = 'https://tomsjavajive.com/shop.php';
require_once __DIR__ . '/includes/header.php';
$productTypesList = db()->fetchAll("SELECT type_id, name, slug FROM product_types WHERE is_active=1 ORDER BY sort_order ASC");
?>
<!-- Page Header -->
<section style="background: linear-gradient(135deg, var(--color-secondary) 0%, #5D2E0A 100%); color: white; padding: 3rem 0; text-align: center;">
<div class="container">
<h1 style="font-family: var(--font-display); font-size: 2.5rem; margin-bottom: 0.5rem;">Our Coffee Collection</h1>
<p style="opacity: 0.9;">Discover your perfect brew from our selection of premium coffees</p>
</div>
</section>
<section class="section">
<div class="container">
<!-- Filters Bar -->
<div style="display: flex; justify-content: space-between; align-items: flex-start; flex-wrap: wrap; gap: 1rem; margin-bottom: 2rem;">
<div style="display: flex; flex-direction: column; gap: 0.75rem;">
<div style="display: flex; gap: 0.75rem; flex-wrap: wrap; align-items: center;">
<a href="/shop.php<?= $subcat ? '?subcat=' . urlencode($subcat) : '' ?>" class="btn <?= !$category ? 'btn-primary' : 'btn-secondary' ?>">All</a>
<?php foreach ($categories as $cat): ?>
<a href="/shop.php?category=<?= urlencode($cat['category']) ?><?= $subcat ? '&subcat=' . urlencode($subcat) : '' ?>"
class="btn <?= $category === $cat['category'] ? 'btn-primary' : 'btn-secondary' ?>">
<?= htmlspecialchars(ucfirst($cat['category'])) ?>
</a>
<?php endforeach; ?>
</div>
<?php if (!empty($productTypesList)): ?>
<div style="display: flex; gap: 0.75rem; flex-wrap: wrap; align-items: center;">
<span class="text-muted" style="font-size: 0.85rem; font-weight: 600; white-space: nowrap;">Sub Categories:</span>
<a href="/shop.php<?= $category ? '?category=' . urlencode($category) : '' ?>" class="btn btn-sm <?= !$subcat ? 'btn-primary' : 'btn-secondary' ?>">All</a>
<?php foreach ($productTypesList as $pt): ?>
<a href="/shop.php?subcat=<?= urlencode($pt['type_id']) ?><?= $category ? '&category=' . urlencode($category) : '' ?>"
class="btn btn-sm <?= $subcat === $pt['type_id'] ? 'btn-primary' : 'btn-secondary' ?>">
<?= htmlspecialchars($pt['name']) ?>
</a>
<?php endforeach; ?>
</div>
<?php endif; ?>
</div>
<div style="display: flex; gap: 1rem; align-items: center;">
<form method="GET" style="display: flex; gap: 0.5rem;">
<?php if ($category): ?>
<input type="hidden" name="category" value="<?= htmlspecialchars($category) ?>">
<?php endif; ?>
<?php if ($subcat): ?>
<input type="hidden" name="subcat" value="<?= htmlspecialchars($subcat) ?>">
<?php endif; ?>
<input type="text" name="search" class="form-input" placeholder="Search..."
value="<?= htmlspecialchars($search) ?>" style="width: 200px;">
<button type="submit" class="btn btn-secondary"><i class="fas fa-search"></i></button>
</form>
<?php
$filterQs = http_build_query(array_filter(['category' => $category, 'subcat' => $subcat]));
$filterQs = $filterQs ? '&' . $filterQs : '';
?>
<select onchange="window.location.href=this.value" class="form-select" style="width: auto;">
<option value="/shop.php?sort=newest<?= $filterQs ?>" <?= $sort === 'newest' ? 'selected' : '' ?>>Newest</option>
<option value="/shop.php?sort=price_low<?= $filterQs ?>" <?= $sort === 'price_low' ? 'selected' : '' ?>>Price: Low to High</option>
<option value="/shop.php?sort=price_high<?= $filterQs ?>" <?= $sort === 'price_high' ? 'selected' : '' ?>>Price: High to Low</option>
<option value="/shop.php?sort=name<?= $filterQs ?>" <?= $sort === 'name' ? 'selected' : '' ?>>Name</option>
</select>
</div>
</div>
<!-- Results count -->
<?php
$subcatName = '';
if ($subcat) {
foreach ($productTypesList as $pt) {
if ($pt['type_id'] === $subcat) { $subcatName = $pt['name']; break; }
}
}
?>
<p class="text-muted" style="margin-bottom: 1.5rem;">
Showing <?= count($products) ?> of <?= $totalProducts ?> products
<?= $category ? ' in ' . htmlspecialchars(ucfirst($category)) : '' ?>
<?= $subcatName ? ' &middot; ' . htmlspecialchars($subcatName) : '' ?>
</p>
<!-- Product Grid -->
<?php if (empty($products)): ?>
<div style="text-align: center; padding: 4rem 0;">
<i class="fas fa-coffee" style="font-size: 4rem; color: var(--color-text-muted); margin-bottom: 1rem;"></i>
<h3>No products found</h3>
<p class="text-muted">Try adjusting your search or filters</p>
<a href="/shop.php" class="btn btn-primary mt-1">View All Products</a>
</div>
<?php else: ?>
<div class="product-grid">
<?php foreach ($products as $product):
$images = json_decode($product['images'] ?? '[]', true);
$imageUrl = !empty($images) ? $images[0] : '/assets/images/placeholder-product.svg';
$price = $product['sale_price'] ?? $product['price'];
?>
<div class="product-card">
<a href="/product.php?id=<?= $product['product_id'] ?>" class="product-card-image">
<img src="<?= htmlspecialchars($imageUrl) ?>" alt="<?= htmlspecialchars($product['name']) ?>">
<?php if ($product['sale_price']): ?>
<span class="product-card-badge">Sale</span>
<?php elseif ($product['is_featured']): ?>
<span class="product-card-badge">Featured</span>
<?php endif; ?>
</a>
<div class="product-card-body">
<?php if ($product['category'] || !empty($product['type_name'])): ?>
<div class="product-card-category">
<?= htmlspecialchars($product['category'] ?? '') ?>
<?php if (!empty($product['type_name'])): ?>
<?= $product['category'] ? ' &middot; ' : '' ?><?= htmlspecialchars($product['type_name']) ?>
<?php endif; ?>
</div>
<?php endif; ?>
<h3 class="product-card-title">
<a href="/product.php?id=<?= $product['product_id'] ?>"><?= htmlspecialchars($product['name']) ?></a>
</h3>
<div class="product-card-price">
<span class="current"><?= formatCurrency($price) ?></span>
<?php if ($product['sale_price']): ?>
<span class="original"><?= formatCurrency($product['price']) ?></span>
<?php endif; ?>
</div>
<button class="btn btn-primary btn-block add-to-cart-btn" data-product-id="<?= $product['product_id'] ?>" onclick="addToCart('<?= $product['product_id'] ?>', 1)">
<i class="fas fa-shopping-bag"></i> Add to Cart
</button>
</div>
</div>
<?php endforeach; ?>
</div>
<!-- Pagination -->
<?php if ($pagination['total_pages'] > 1): ?>
<?= renderPagination($pagination, '/shop.php?' . http_build_query(array_filter(['category' => $category, 'sort' => $sort]))) ?>
<?php endif; ?>
<?php endif; ?>
</div>
</section>
<?php require_once __DIR__ . '/includes/footer.php'; ?>