Files
2026-05-16 23:00:37 -05:00

291 lines
12 KiB
PHP

<?php
ob_start();
/**
* Tom's Java Jive - Admin Products Page
*/
$pageTitle = 'Products';
require_once __DIR__ . '/includes/header.php';
// Handle actions
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$action = $_POST['action'] ?? '';
if ($action === 'delete' && !empty($_POST['product_id'])) {
db()->delete('products', 'product_id = :id', ['id' => $_POST['product_id']]);
setFlash('success', 'Product deleted successfully');
header('Location: /admin/products.php');
exit;
}
if ($action === 'bulk_delete' && !empty($_POST['product_ids'])) {
$ids = $_POST['product_ids'];
$placeholders = implode(',', array_fill(0, count($ids), '?'));
db()->query("DELETE FROM products WHERE product_id IN ($placeholders)", $ids);
setFlash('success', count($ids) . ' products deleted');
header('Location: /admin/products.php');
exit;
}
if ($action === 'toggle_status' && !empty($_POST['product_id'])) {
$product = db()->fetch("SELECT is_active FROM products WHERE product_id = :id", ['id' => $_POST['product_id']]);
if ($product) {
db()->update('products', ['is_active' => !$product['is_active']], 'product_id = :id', ['id' => $_POST['product_id']]);
setFlash('success', 'Product status updated');
}
header('Location: /admin/products.php');
exit;
}
}
// Filters
$search = $_GET['search'] ?? '';
$category = $_GET['category'] ?? '';
$status = $_GET['status'] ?? '';
$page = max(1, intval($_GET['page'] ?? 1));
// Build query
$where = ['1=1'];
$params = [];
if ($search) {
$where[] = '(name LIKE :search OR sku LIKE :search)';
$params['search'] = '%' . $search . '%';
}
if ($category) {
$where[] = 'category = :category';
$params['category'] = $category;
}
if ($status === 'active') {
$where[] = 'is_active = 1';
} elseif ($status === 'inactive') {
$where[] = 'is_active = 0';
} elseif ($status === 'low_stock') {
$where[] = 'stock <= low_stock_threshold';
}
$whereClause = implode(' AND ', $where);
// Get total and paginate
$totalProducts = db()->count('products', $whereClause, $params);
$pagination = paginate($totalProducts, $page, ADMIN_ITEMS_PER_PAGE);
// Get products
$products = db()->fetchAll(
"SELECT * FROM products WHERE {$whereClause} ORDER BY created_at DESC LIMIT :limit OFFSET :offset",
array_merge($params, ['limit' => $pagination['per_page'], 'offset' => $pagination['offset']])
);
// Get categories
$categories = db()->fetchAll(
"SELECT DISTINCT category FROM products WHERE category IS NOT NULL AND category != '' ORDER BY category"
);
?>
<div class="page-header">
<h1 class="page-title">Products</h1>
<a href="/admin/product-edit.php" class="btn btn-primary">
<i class="fas fa-plus"></i> Add Product
</a>
</div>
<?php if (hasFlash('success')): ?>
<div class="alert alert-success">
<i class="fas fa-check-circle"></i>
<?= getFlash('success') ?>
</div>
<?php endif; ?>
<!-- Filters -->
<div class="admin-card">
<div class="admin-card-body">
<form method="GET" style="display: flex; gap: 1rem; flex-wrap: wrap; align-items: end;">
<div class="form-group mb-0" style="flex: 1; min-width: 200px;">
<label class="form-label">Search</label>
<input type="text" name="search" class="form-input" placeholder="Name or SKU..."
value="<?= htmlspecialchars($search) ?>">
</div>
<div class="form-group mb-0">
<label class="form-label">Category</label>
<select name="category" class="form-select">
<option value="">All Categories</option>
<?php foreach ($categories as $cat): ?>
<option value="<?= htmlspecialchars($cat['category']) ?>"
<?= $category === $cat['category'] ? 'selected' : '' ?>>
<?= htmlspecialchars(ucfirst($cat['category'])) ?>
</option>
<?php endforeach; ?>
</select>
</div>
<div class="form-group mb-0">
<label class="form-label">Status</label>
<select name="status" class="form-select">
<option value="">All</option>
<option value="active" <?= $status === 'active' ? 'selected' : '' ?>>Active</option>
<option value="inactive" <?= $status === 'inactive' ? 'selected' : '' ?>>Inactive</option>
<option value="low_stock" <?= $status === 'low_stock' ? 'selected' : '' ?>>Low Stock</option>
</select>
</div>
<button type="submit" class="btn btn-secondary">
<i class="fas fa-filter"></i> Filter
</button>
<?php if ($search || $category || $status): ?>
<a href="/admin/products.php" class="btn btn-secondary">Clear</a>
<?php endif; ?>
</form>
</div>
</div>
<!-- Products Table -->
<div class="admin-card">
<div class="admin-card-header">
<span><?= $totalProducts ?> products found</span>
<div id="bulkActions" style="display: none;">
<form method="POST" style="display: inline;">
<input type="hidden" name="action" value="bulk_delete">
<input type="hidden" name="product_ids" id="selectedIds">
<button type="submit" class="btn btn-sm btn-danger" data-confirm="Delete selected products?">
<i class="fas fa-trash"></i> Delete Selected
</button>
</form>
</div>
</div>
<div class="admin-card-body" style="padding: 0;">
<table class="admin-table">
<thead>
<tr>
<th style="width: 40px;">
<input type="checkbox" id="selectAll">
</th>
<th style="width: 60px;">Image</th>
<th>Product</th>
<th>SKU</th>
<th>Category</th>
<th>Price</th>
<th>Stock</th>
<th>Status</th>
<th style="width: 120px;">Actions</th>
</tr>
</thead>
<tbody>
<?php if (empty($products)): ?>
<tr>
<td colspan="9" class="text-muted" style="text-align: center; padding: 2rem;">
No products found
</td>
</tr>
<?php else: ?>
<?php foreach ($products as $product):
$images = json_decode($product['images'] ?? '[]', true);
$imageUrl = !empty($images) ? $images[0] : '/assets/images/placeholder-product.jpg';
?>
<tr>
<td>
<input type="checkbox" class="product-checkbox" value="<?= $product['product_id'] ?>">
</td>
<td>
<img src="<?= htmlspecialchars($imageUrl) ?>" alt=""
style="width: 50px; height: 50px; object-fit: cover; border-radius: 4px;">
</td>
<td>
<strong><?= htmlspecialchars($product['name']) ?></strong>
<?php if ($product['is_featured']): ?>
<span class="badge badge-primary">Featured</span>
<?php endif; ?>
</td>
<td class="text-muted"><?= htmlspecialchars($product['sku'] ?? '-') ?></td>
<td><?= htmlspecialchars(ucfirst($product['category'] ?? '-')) ?></td>
<td>
<?php if ($product['sale_price'] && $product['sale_price'] < $product['price']): ?>
<span style="text-decoration: line-through; color: var(--admin-text-light);">
<?= formatCurrency($product['price']) ?>
</span><br>
<strong class="text-success"><?= formatCurrency($product['sale_price']) ?></strong>
<?php else: ?>
<?= formatCurrency($product['price']) ?>
<?php endif; ?>
</td>
<td>
<?php if ($product['stock'] <= 0): ?>
<span class="badge badge-error">Out of Stock</span>
<?php elseif ($product['stock'] <= $product['low_stock_threshold']): ?>
<span class="badge badge-warning"><?= $product['stock'] ?> left</span>
<?php else: ?>
<span class="text-success"><?= $product['stock'] ?></span>
<?php endif; ?>
</td>
<td>
<?php if ($product['is_active']): ?>
<span class="badge badge-success">Active</span>
<?php else: ?>
<span class="badge badge-error">Inactive</span>
<?php endif; ?>
</td>
<td>
<a href="/admin/product-edit.php?id=<?= $product['product_id'] ?>"
class="btn btn-sm btn-secondary" title="Edit">
<i class="fas fa-edit"></i>
</a>
<form method="POST" style="display: inline;">
<input type="hidden" name="action" value="toggle_status">
<input type="hidden" name="product_id" value="<?= $product['product_id'] ?>">
<button type="submit" class="btn btn-sm btn-secondary"
title="<?= $product['is_active'] ? 'Deactivate' : 'Activate' ?>">
<i class="fas fa-<?= $product['is_active'] ? 'eye-slash' : 'eye' ?>"></i>
</button>
</form>
<form method="POST" style="display: inline;">
<input type="hidden" name="action" value="delete">
<input type="hidden" name="product_id" value="<?= $product['product_id'] ?>">
<button type="submit" class="btn btn-sm btn-danger"
data-confirm="Delete this product?" title="Delete">
<i class="fas fa-trash"></i>
</button>
</form>
</td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
<!-- Pagination -->
<?php if ($pagination['total_pages'] > 1): ?>
<div style="display: flex; justify-content: center; margin-top: 1rem;">
<?= renderPagination($pagination, '/admin/products.php?search=' . urlencode($search) . '&category=' . urlencode($category) . '&status=' . $status) ?>
</div>
<?php endif; ?>
<script>
// Bulk selection
const selectAll = document.getElementById('selectAll');
const checkboxes = document.querySelectorAll('.product-checkbox');
const bulkActions = document.getElementById('bulkActions');
const selectedIds = document.getElementById('selectedIds');
selectAll.addEventListener('change', function() {
checkboxes.forEach(cb => cb.checked = this.checked);
updateBulkActions();
});
checkboxes.forEach(cb => {
cb.addEventListener('change', updateBulkActions);
});
function updateBulkActions() {
const checked = document.querySelectorAll('.product-checkbox:checked');
bulkActions.style.display = checked.length > 0 ? 'block' : 'none';
selectedIds.value = Array.from(checked).map(cb => cb.value).join(',');
}
</script>
<?php require_once __DIR__ . '/includes/footer.php'; ?>