mirror of
https://github.com/myronblair/tomsjavajive
synced 2026-06-30 09:40:24 -05:00
771e1a15b1
Split style.css into home.css (hero, features, newsletter, splash) and products.css (product grid/cards). Each page loads only what it needs via $extraHead. style.css now contains only truly shared styles. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
211 lines
12 KiB
PHP
211 lines
12 KiB
PHP
<?php
|
|
/**
|
|
* Tom's Java Jive - Shop Page
|
|
*/
|
|
|
|
$pageTitle = "Shop - Tom's Java Jive";
|
|
$extraHead = '<link rel="stylesheet" href="/assets/css/products.css?v=' . filemtime(__DIR__ . '/assets/css/products.css') . '">';
|
|
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");
|
|
|
|
?>
|
|
|
|
<?php
|
|
$filterQs = http_build_query(array_filter(['category' => $category, 'subcat' => $subcat]));
|
|
$filterQs = $filterQs ? '&' . $filterQs : '';
|
|
?>
|
|
|
|
<!-- Shop Header + Filters combined -->
|
|
<section style="background: linear-gradient(135deg, var(--color-secondary) 0%, #5D2E0A 100%); color: white; padding: 1.25rem 0;">
|
|
<div class="container">
|
|
<div style="display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 0.75rem;">
|
|
<h1 style="font-family: var(--font-display); font-size: 1.4rem; margin: 0; white-space: nowrap;">Our Coffee Collection</h1>
|
|
<div style="display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap;">
|
|
<form method="GET" style="display: flex; gap: 0.4rem;">
|
|
<?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: 160px; padding: 0.35rem 0.75rem; font-size: 0.85rem; background: rgba(255,255,255,0.15); border-color: rgba(255,255,255,0.3); color: white;">
|
|
<button type="submit" style="background: rgba(255,255,255,0.2); border: 1px solid rgba(255,255,255,0.3); color: white; padding: 0.35rem 0.65rem; border-radius: var(--radius-md); cursor: pointer;">
|
|
<i class="fas fa-search" style="font-size: 0.8rem;"></i>
|
|
</button>
|
|
</form>
|
|
<select onchange="window.location.href=this.value"
|
|
style="background: rgba(255,255,255,0.15); border: 1px solid rgba(255,255,255,0.3); color: white; padding: 0.35rem 0.6rem; border-radius: var(--radius-md); font-size: 0.85rem; cursor: pointer;">
|
|
<option value="/shop.php?sort=newest<?= $filterQs ?>" <?= $sort==='newest' ? 'selected' : '' ?> style="color:#333">Newest</option>
|
|
<option value="/shop.php?sort=price_low<?= $filterQs ?>" <?= $sort==='price_low' ? 'selected' : '' ?> style="color:#333">Price ↑</option>
|
|
<option value="/shop.php?sort=price_high<?= $filterQs ?>" <?= $sort==='price_high' ? 'selected' : '' ?> style="color:#333">Price ↓</option>
|
|
<option value="/shop.php?sort=name<?= $filterQs ?>" <?= $sort==='name' ? 'selected' : '' ?> style="color:#333">Name</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Filter pills -->
|
|
<div style="display: flex; flex-wrap: wrap; align-items: center; gap: 0.4rem; margin-top: 0.75rem;">
|
|
<span style="font-size: 0.75rem; opacity: 0.75; text-transform: uppercase; letter-spacing: 0.05em; margin-right: 0.25rem;">Category:</span>
|
|
<a href="/shop.php<?= $subcat ? '?subcat='.urlencode($subcat) : '' ?>"
|
|
style="<?= !$category ? 'background:white;color:var(--color-secondary);font-weight:700;' : 'background:rgba(255,255,255,0.15);color:white;' ?> padding:0.2rem 0.65rem; border-radius:999px; font-size:0.8rem; text-decoration:none; border:1px solid rgba(255,255,255,0.4);">All</a>
|
|
<?php foreach ($categories as $cat): ?>
|
|
<a href="/shop.php?category=<?= urlencode($cat['category']) ?><?= $subcat ? '&subcat='.urlencode($subcat) : '' ?>"
|
|
style="<?= $category===$cat['category'] ? 'background:white;color:var(--color-secondary);font-weight:700;' : 'background:rgba(255,255,255,0.15);color:white;' ?> padding:0.2rem 0.65rem; border-radius:999px; font-size:0.8rem; text-decoration:none; border:1px solid rgba(255,255,255,0.4);">
|
|
<?= htmlspecialchars(ucfirst($cat['category'])) ?>
|
|
</a>
|
|
<?php endforeach; ?>
|
|
|
|
<?php if (!empty($productTypesList)): ?>
|
|
<span style="width:1px; height:16px; background:rgba(255,255,255,0.3); margin:0 0.25rem;"></span>
|
|
<span style="font-size:0.75rem; opacity:0.75; text-transform:uppercase; letter-spacing:0.05em; margin-right:0.25rem;">Type:</span>
|
|
<a href="/shop.php<?= $category ? '?category='.urlencode($category) : '' ?>"
|
|
style="<?= !$subcat ? 'background:white;color:var(--color-secondary);font-weight:700;' : 'background:rgba(255,255,255,0.15);color:white;' ?> padding:0.2rem 0.65rem; border-radius:999px; font-size:0.8rem; text-decoration:none; border:1px solid rgba(255,255,255,0.4);">All</a>
|
|
<?php foreach ($productTypesList as $pt): ?>
|
|
<a href="/shop.php?subcat=<?= urlencode($pt['type_id']) ?><?= $category ? '&category='.urlencode($category) : '' ?>"
|
|
style="<?= $subcat===$pt['type_id'] ? 'background:white;color:var(--color-secondary);font-weight:700;' : 'background:rgba(255,255,255,0.15);color:white;' ?> padding:0.2rem 0.65rem; border-radius:999px; font-size:0.8rem; text-decoration:none; border:1px solid rgba(255,255,255,0.4);">
|
|
<?= htmlspecialchars($pt['name']) ?>
|
|
</a>
|
|
<?php endforeach; ?>
|
|
<?php endif; ?>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<section class="section" style="padding-top: 1.5rem;">
|
|
<div class="container">
|
|
|
|
<!-- 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 ? ' · ' . 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'] ? ' · ' : '' ?><?= 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'; ?>
|