157 lines
6.1 KiB
PHP
157 lines
6.1 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
|
|
require_once __DIR__ . '/includes/layout.php';
|
|
|
|
ensure_tables();
|
|
|
|
$stores = get_stores();
|
|
|
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|
$storeId = (int)($_POST['store_id'] ?? 0);
|
|
$sku = trim($_POST['sku'] ?? '');
|
|
$name = trim($_POST['name'] ?? '');
|
|
$price = (float)($_POST['price'] ?? 0);
|
|
$stock = (int)($_POST['stock'] ?? 0);
|
|
$status = trim($_POST['status'] ?? 'active');
|
|
|
|
if (!$storeId) {
|
|
flash_set('error', 'Select a store for the product.');
|
|
} elseif ($sku === '' || $name === '') {
|
|
flash_set('error', 'SKU and product name are required.');
|
|
} elseif ($price < 0 || $stock < 0) {
|
|
flash_set('error', 'Price and stock must be zero or greater.');
|
|
} else {
|
|
$stmt = db()->prepare(
|
|
"INSERT INTO products (store_id, sku, name, price, stock, status, created_at)
|
|
VALUES (:store_id, :sku, :name, :price, :stock, :status, :created_at)"
|
|
);
|
|
$stmt->bindValue(':store_id', $storeId, PDO::PARAM_INT);
|
|
$stmt->bindValue(':sku', $sku);
|
|
$stmt->bindValue(':name', $name);
|
|
$stmt->bindValue(':price', $price);
|
|
$stmt->bindValue(':stock', $stock, PDO::PARAM_INT);
|
|
$stmt->bindValue(':status', $status);
|
|
$stmt->bindValue(':created_at', date('Y-m-d H:i:s'));
|
|
$stmt->execute();
|
|
flash_set('success', 'Product added to the catalog.');
|
|
}
|
|
|
|
header('Location: /products.php');
|
|
exit;
|
|
}
|
|
|
|
$filterStore = isset($_GET['store_id']) ? (int)$_GET['store_id'] : null;
|
|
$products = get_products($filterStore, 50);
|
|
|
|
render_header('Product Catalog', 'Manage SKUs, pricing, and inventory for every store.');
|
|
?>
|
|
|
|
<section class="row g-4">
|
|
<div class="col-12 col-lg-4">
|
|
<div class="content-card h-100">
|
|
<h4 class="mb-3">Add product</h4>
|
|
<?php if (empty($stores)): ?>
|
|
<div class="alert alert-warning mb-0">Create a store first to add products.</div>
|
|
<?php else: ?>
|
|
<form method="post" class="vstack gap-3">
|
|
<div>
|
|
<label class="form-label">Store</label>
|
|
<select name="store_id" class="form-select" required>
|
|
<option value="">Select store</option>
|
|
<?php foreach ($stores as $store): ?>
|
|
<option value="<?= h((string)$store['id']) ?>"><?= h($store['name']) ?></option>
|
|
<?php endforeach; ?>
|
|
</select>
|
|
</div>
|
|
<div>
|
|
<label class="form-label">SKU</label>
|
|
<input type="text" name="sku" class="form-control" placeholder="SKU-001" required>
|
|
</div>
|
|
<div>
|
|
<label class="form-label">Product name</label>
|
|
<input type="text" name="name" class="form-control" placeholder="Walnut Desk Lamp" required>
|
|
</div>
|
|
<div class="row g-2">
|
|
<div class="col-6">
|
|
<label class="form-label">Price</label>
|
|
<input type="number" step="0.01" name="price" class="form-control" value="0.00" min="0">
|
|
</div>
|
|
<div class="col-6">
|
|
<label class="form-label">Stock</label>
|
|
<input type="number" name="stock" class="form-control" value="0" min="0">
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<label class="form-label">Status</label>
|
|
<select name="status" class="form-select">
|
|
<option value="active">Active</option>
|
|
<option value="paused">Paused</option>
|
|
<option value="archived">Archived</option>
|
|
</select>
|
|
</div>
|
|
<button class="btn btn-dark w-100" type="submit">Add product</button>
|
|
</form>
|
|
<?php endif; ?>
|
|
<p class="text-muted small mt-3 mb-0">Assign every SKU to a store to keep inventory separated.</p>
|
|
</div>
|
|
</div>
|
|
<div class="col-12 col-lg-8">
|
|
<div class="content-card">
|
|
<div class="d-flex flex-wrap align-items-center justify-content-between mb-3 gap-2">
|
|
<div>
|
|
<h4 class="mb-1">Catalog list</h4>
|
|
<p class="text-muted small mb-0">Filter by store to narrow inventory.</p>
|
|
</div>
|
|
<form method="get" class="d-flex align-items-center gap-2">
|
|
<select name="store_id" class="form-select form-select-sm">
|
|
<option value="">All stores</option>
|
|
<?php foreach ($stores as $store): ?>
|
|
<option value="<?= h((string)$store['id']) ?>" <?= $filterStore === (int)$store['id'] ? 'selected' : '' ?>>
|
|
<?= h($store['name']) ?>
|
|
</option>
|
|
<?php endforeach; ?>
|
|
</select>
|
|
<button class="btn btn-sm btn-outline-dark" type="submit">Filter</button>
|
|
</form>
|
|
</div>
|
|
|
|
<?php if (empty($products)): ?>
|
|
<div class="empty-state">
|
|
<p class="mb-2">No products match this filter.</p>
|
|
<p class="text-muted small mb-0">Add a new SKU to get started.</p>
|
|
</div>
|
|
<?php else: ?>
|
|
<div class="table-responsive">
|
|
<table class="table align-middle">
|
|
<thead>
|
|
<tr>
|
|
<th>SKU</th>
|
|
<th>Product</th>
|
|
<th>Store</th>
|
|
<th>Status</th>
|
|
<th class="text-end">Price</th>
|
|
<th class="text-end">Stock</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<?php foreach ($products as $product): ?>
|
|
<tr>
|
|
<td class="text-muted"><?= h($product['sku']) ?></td>
|
|
<td><a class="link-dark" href="/product.php?id=<?= h((string)$product['id']) ?>"><?= h($product['name']) ?></a></td>
|
|
<td><?= h($product['store_name']) ?></td>
|
|
<td><span class="badge bg-light text-dark border"><?= h(ucfirst($product['status'])) ?></span></td>
|
|
<td class="text-end">$<?= h(number_format((float)$product['price'], 2)) ?></td>
|
|
<td class="text-end"><?= h((string)$product['stock']) ?></td>
|
|
</tr>
|
|
<?php endforeach; ?>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
<?php endif; ?>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<?php render_footer(); ?>
|