mirror of
https://github.com/myronblair/tomsjavajive
synced 2026-06-30 17:50:32 -05:00
291 lines
12 KiB
PHP
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'; ?>
|