429 lines
16 KiB
PHP
429 lines
16 KiB
PHP
<?php
|
|
include("../db.php");
|
|
|
|
if (!isset($_SESSION['rol']) || $_SESSION['rol'] !== 'admin') {
|
|
header("Location: ../login.php?error=acceso_denegado");
|
|
exit();
|
|
}
|
|
|
|
$mensaje = "";
|
|
$tipo_mensaje = "";
|
|
if (isset($_GET['status'])) {
|
|
if ($_GET['status'] === 'ok_manual') {
|
|
$mensaje = "¡Producto agregado con éxito!";
|
|
$tipo_mensaje = "success";
|
|
} elseif ($_GET['status'] === 'ok_csv') {
|
|
$cant = isset($_GET['cant']) ? (int)$_GET['cant'] : 0;
|
|
$mensaje = "Catálogo actualizado — $cant productos procesados desde el CSV.";
|
|
$tipo_mensaje = "success";
|
|
} elseif ($_GET['status'] === 'error') {
|
|
$mensaje = "Hubo un problema al procesar la solicitud. Verificá los formatos de los datos.";
|
|
$tipo_mensaje = "error";
|
|
}
|
|
}
|
|
|
|
$query_productos = "SELECT id, nombre, precio, stock, imagen, categoria FROM productos WHERE activo = 1 ORDER BY categoria ASC, nombre ASC";
|
|
$res_productos = mysqli_query($conexion, $query_productos);
|
|
$todos_productos = mysqli_fetch_all($res_productos, MYSQLI_ASSOC);
|
|
|
|
// Categorías únicas para los filtros
|
|
$categorias_unicas = array_unique(array_column($todos_productos, 'categoria'));
|
|
sort($categorias_unicas);
|
|
?>
|
|
<!DOCTYPE html>
|
|
<html lang="es">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Inventario - La Pecosa</title>
|
|
<link rel="stylesheet" href="../css/all.min.css">
|
|
<link rel="stylesheet" href="admin_style.css">
|
|
<style>
|
|
/* ── ALERT ── */
|
|
.alert-ui {
|
|
padding: 13px 16px;
|
|
border-radius: 8px;
|
|
margin-bottom: 22px;
|
|
font-weight: 600;
|
|
font-size: 0.875rem;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
}
|
|
.alert-ui.success { background: #f0fdf4; color: #166534; border: 1px solid #bbf7d0; }
|
|
.alert-ui.error { background: #fef2f2; color: #991b1b; border: 1px solid #fee2e2; }
|
|
|
|
/* ── GRID FORMULARIOS ── */
|
|
.grid-forms {
|
|
display: grid;
|
|
grid-template-columns: 1fr 1fr;
|
|
gap: 20px;
|
|
margin-bottom: 28px;
|
|
}
|
|
@media (max-width: 860px) { .grid-forms { grid-template-columns: 1fr; } }
|
|
|
|
.form-card {
|
|
background: #fff;
|
|
border: 1px solid #e5e7eb;
|
|
border-radius: 12px;
|
|
padding: 22px 24px;
|
|
}
|
|
.form-card-title {
|
|
font-size: 0.95rem;
|
|
font-weight: 700;
|
|
color: #111827;
|
|
margin-bottom: 18px;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
}
|
|
.form-card-title .fc-icon {
|
|
width: 28px;
|
|
height: 28px;
|
|
border-radius: 6px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
font-size: 0.8rem;
|
|
flex-shrink: 0;
|
|
}
|
|
.fc-icon.red { background: #fee2e2; color: #dc2626; }
|
|
.fc-icon.green { background: #dcfce7; color: #16a34a; }
|
|
|
|
/* ── FORM FIELDS ── */
|
|
.fg { margin-bottom: 13px; }
|
|
.fg label {
|
|
display: block;
|
|
font-size: 0.8rem;
|
|
font-weight: 600;
|
|
color: #374151;
|
|
margin-bottom: 5px;
|
|
}
|
|
.fg input[type="text"],
|
|
.fg input[type="number"],
|
|
.fg input[type="file"],
|
|
.fg select {
|
|
width: 100%;
|
|
padding: 9px 11px;
|
|
border: 1px solid #d1d5db;
|
|
border-radius: 7px;
|
|
font-size: 0.875rem;
|
|
outline: none;
|
|
transition: border-color 0.2s;
|
|
box-sizing: border-box;
|
|
}
|
|
.fg input:focus, .fg select:focus { border-color: #dc2626; }
|
|
|
|
.fg-row { display: flex; gap: 12px; }
|
|
.fg-row .fg { flex: 1; }
|
|
|
|
.csv-hint {
|
|
background: #f0fdf4;
|
|
border-left: 3px solid #16a34a;
|
|
border-radius: 0 6px 6px 0;
|
|
padding: 10px 12px;
|
|
font-size: 0.8rem;
|
|
color: #166534;
|
|
margin-bottom: 14px;
|
|
line-height: 1.5;
|
|
}
|
|
.csv-hint code {
|
|
display: block;
|
|
margin-top: 4px;
|
|
font-family: monospace;
|
|
font-size: 0.78rem;
|
|
letter-spacing: 0.2px;
|
|
}
|
|
|
|
.btn-submit {
|
|
width: 100%;
|
|
padding: 11px;
|
|
border: none;
|
|
border-radius: 7px;
|
|
font-weight: 700;
|
|
font-size: 0.9rem;
|
|
cursor: pointer;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
gap: 7px;
|
|
transition: opacity 0.15s, transform 0.15s;
|
|
margin-top: 4px;
|
|
}
|
|
.btn-submit:hover { opacity: 0.88; transform: translateY(-1px); }
|
|
.btn-submit.red { background: #dc2626; color: #fff; }
|
|
.btn-submit.green { background: #16a34a; color: #fff; }
|
|
|
|
/* ── SECCIÓN CATÁLOGO ── */
|
|
.catalogo-card {
|
|
background: #fff;
|
|
border: 1px solid #e5e7eb;
|
|
border-radius: 12px;
|
|
overflow: hidden;
|
|
}
|
|
.catalogo-header {
|
|
padding: 16px 22px;
|
|
border-bottom: 1px solid #f3f4f6;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
flex-wrap: wrap;
|
|
gap: 12px;
|
|
}
|
|
.catalogo-header h2 {
|
|
font-size: 1rem;
|
|
font-weight: 700;
|
|
color: #111827;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 7px;
|
|
margin: 0;
|
|
}
|
|
.catalogo-header h2 i { color: #dc2626; }
|
|
|
|
/* ── FILTROS DE CATEGORÍA ── */
|
|
.cat-filters {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 6px;
|
|
}
|
|
.cat-btn {
|
|
background: #f3f4f6;
|
|
border: 1px solid #e5e7eb;
|
|
border-radius: 20px;
|
|
padding: 5px 13px;
|
|
font-size: 0.78rem;
|
|
font-weight: 600;
|
|
color: #4b5563;
|
|
cursor: pointer;
|
|
transition: all 0.15s;
|
|
white-space: nowrap;
|
|
}
|
|
.cat-btn:hover { background: #fee2e2; border-color: #fca5a5; color: #dc2626; }
|
|
.cat-btn.active { background: #dc2626; border-color: #dc2626; color: #fff; }
|
|
|
|
/* ── CONTADOR ── */
|
|
.cat-count {
|
|
font-size: 0.78rem;
|
|
color: #9ca3af;
|
|
padding: 6px 22px;
|
|
border-bottom: 1px solid #f3f4f6;
|
|
}
|
|
#cat-count-num { font-weight: 700; color: #374151; }
|
|
|
|
/* ── TABLA ── */
|
|
.admin-table td { font-size: 0.875rem; vertical-align: middle; }
|
|
.admin-table tr:last-child td { border-bottom: none; }
|
|
|
|
.img-preview {
|
|
width: 46px;
|
|
height: 46px;
|
|
object-fit: cover;
|
|
border-radius: 7px;
|
|
border: 1px solid #e5e7eb;
|
|
}
|
|
.cat-chip {
|
|
background: #f1f5f9;
|
|
color: #475569;
|
|
padding: 3px 8px;
|
|
border-radius: 4px;
|
|
font-size: 0.78rem;
|
|
font-weight: 600;
|
|
}
|
|
.stock-ok { color: #374151; }
|
|
.stock-warn { color: #92400e; font-weight: 700; }
|
|
.stock-zero { color: #991b1b; font-weight: 700; }
|
|
|
|
tr.sin-stock { background: #fff5f5; }
|
|
|
|
.no-results {
|
|
text-align: center;
|
|
color: #9ca3af;
|
|
padding: 40px 14px;
|
|
font-size: 0.9rem;
|
|
}
|
|
.no-results i { font-size: 1.5rem; display: block; margin-bottom: 8px; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
|
|
<?php include("sidebar.php"); ?>
|
|
|
|
<div class="main-content">
|
|
|
|
<div class="page-header">
|
|
<h1><i class="fa-solid fa-boxes-stacked"></i> Gestión de inventario y menú</h1>
|
|
</div>
|
|
|
|
<?php if (!empty($mensaje)): ?>
|
|
<div class="alert-ui <?php echo $tipo_mensaje; ?>">
|
|
<i class="fa-solid <?php echo $tipo_mensaje === 'success' ? 'fa-circle-check' : 'fa-circle-exclamation'; ?>"></i>
|
|
<?php echo $mensaje; ?>
|
|
</div>
|
|
<?php endif; ?>
|
|
|
|
<!-- FORMULARIOS -->
|
|
<div class="grid-forms">
|
|
|
|
<!-- Agregar producto manual -->
|
|
<div class="form-card">
|
|
<div class="form-card-title">
|
|
<span class="fc-icon red"><i class="fa-solid fa-plus"></i></span>
|
|
Agregar un producto
|
|
</div>
|
|
<form action="procesar_manual.php" method="POST" enctype="multipart/form-data">
|
|
<div class="fg">
|
|
<label>Nombre del producto</label>
|
|
<input type="text" name="nombre" placeholder="Ej: Pizza especial La Pecosa" required>
|
|
</div>
|
|
<div class="fg">
|
|
<label>Categoría</label>
|
|
<select name="categoria" required>
|
|
<option value="Carne">Carne</option>
|
|
<option value="Verdura">Verdura</option>
|
|
<option value="Vinoteca">Vinoteca</option>
|
|
<option value="Bebidas">Bebidas</option>
|
|
<option value="Lácteos">Lácteos</option>
|
|
<option value="Panadería">Panadería</option>
|
|
<option value="Limpieza">Limpieza</option>
|
|
<option value="Almacén">Almacén</option>
|
|
</select>
|
|
</div>
|
|
<div class="fg-row">
|
|
<div class="fg">
|
|
<label>Precio ($)</label>
|
|
<input type="number" name="precio" placeholder="4500" min="0" step="0.01" required>
|
|
</div>
|
|
<div class="fg">
|
|
<label>Stock inicial</label>
|
|
<input type="number" name="stock" placeholder="20" min="0" required>
|
|
</div>
|
|
</div>
|
|
<div class="fg">
|
|
<label>Foto del producto</label>
|
|
<input type="file" name="imagen" accept="image/*" required>
|
|
</div>
|
|
<button type="submit" class="btn-submit red">
|
|
<i class="fa-solid fa-floppy-disk"></i> Guardar en menú
|
|
</button>
|
|
</form>
|
|
</div>
|
|
|
|
<!-- Carga masiva CSV -->
|
|
<div class="form-card" style="display:flex; flex-direction:column; justify-content:space-between;">
|
|
<div>
|
|
<div class="form-card-title">
|
|
<span class="fc-icon green"><i class="fa-solid fa-file-csv"></i></span>
|
|
Carga masiva del catálogo (.csv)
|
|
</div>
|
|
<p style="font-size:0.855rem; color:#6b7280; line-height:1.55; margin-bottom:14px;">
|
|
Armá el Excel y guardalo como CSV para actualizar precios o stocks de forma masiva sin cargar producto por producto.
|
|
</p>
|
|
<div class="csv-hint">
|
|
<strong>Estructura de columnas requerida (separado por punto y coma):</strong>
|
|
<code>id ; nombre ; precio ; categoria ; imagen ; activo ; stock</code>
|
|
</div>
|
|
</div>
|
|
<form action="importar_csv.php" method="POST" enctype="multipart/form-data">
|
|
<div class="fg">
|
|
<label>Seleccionar archivo CSV</label>
|
|
<input type="file" name="archivo_csv" accept=".csv" required>
|
|
</div>
|
|
<button type="submit" class="btn-submit green">
|
|
<i class="fa-solid fa-upload"></i> Procesar catálogo
|
|
</button>
|
|
</form>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<!-- CATÁLOGO CON FILTROS -->
|
|
<div class="catalogo-card">
|
|
<div class="catalogo-header">
|
|
<h2><i class="fa-solid fa-list"></i> Productos en la plataforma</h2>
|
|
<div class="cat-filters">
|
|
<button class="cat-btn active" onclick="filtrarCat(this, 'todos')">Todos</button>
|
|
<?php foreach ($categorias_unicas as $cat): ?>
|
|
<button class="cat-btn" onclick="filtrarCat(this, '<?php echo htmlspecialchars($cat); ?>')">
|
|
<?php echo htmlspecialchars($cat); ?>
|
|
</button>
|
|
<?php endforeach; ?>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="cat-count">
|
|
Mostrando <span id="cat-count-num"><?php echo count($todos_productos); ?></span> productos
|
|
</div>
|
|
|
|
<div style="overflow-x:auto;">
|
|
<table class="admin-table" id="tabla-productos">
|
|
<thead>
|
|
<tr>
|
|
<th>Foto</th>
|
|
<th>ID</th>
|
|
<th>Nombre</th>
|
|
<th>Categoría</th>
|
|
<th>Precio</th>
|
|
<th>Stock</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="tbody-productos">
|
|
<?php foreach ($todos_productos as $prod):
|
|
$sin_stock = ($prod['stock'] <= 0);
|
|
$stock_clase = $sin_stock ? 'stock-zero' : ($prod['stock'] <= 5 ? 'stock-warn' : 'stock-ok');
|
|
?>
|
|
<tr class="prod-row <?php echo $sin_stock ? 'sin-stock' : ''; ?>"
|
|
data-cat="<?php echo htmlspecialchars($prod['categoria']); ?>">
|
|
<td>
|
|
<img src="../<?php echo htmlspecialchars($prod['imagen']); ?>"
|
|
class="img-preview" alt="<?php echo htmlspecialchars($prod['nombre']); ?>">
|
|
</td>
|
|
<td style="color:#9ca3af;">#<?php echo $prod['id']; ?></td>
|
|
<td><strong><?php echo htmlspecialchars($prod['nombre']); ?></strong></td>
|
|
<td><span class="cat-chip"><?php echo htmlspecialchars($prod['categoria']); ?></span></td>
|
|
<td>$<?php echo number_format($prod['precio'], 2, ',', '.'); ?></td>
|
|
<td class="<?php echo $stock_clase; ?>">
|
|
<?php if ($sin_stock): ?>
|
|
<i class="fa-solid fa-triangle-exclamation"></i> Sin stock
|
|
<?php elseif ($prod['stock'] <= 5): ?>
|
|
<i class="fa-solid fa-circle-exclamation"></i> <?php echo $prod['stock']; ?> un.
|
|
<?php else: ?>
|
|
<?php echo $prod['stock']; ?> un.
|
|
<?php endif; ?>
|
|
</td>
|
|
</tr>
|
|
<?php endforeach; ?>
|
|
</tbody>
|
|
</table>
|
|
|
|
<div id="no-results" class="no-results" style="display:none;">
|
|
<i class="fa-solid fa-box-open"></i>
|
|
No hay productos en esta categoría.
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<script>
|
|
function filtrarCat(btn, cat) {
|
|
// Actualizar botón activo
|
|
document.querySelectorAll('.cat-btn').forEach(b => b.classList.remove('active'));
|
|
btn.classList.add('active');
|
|
|
|
const filas = document.querySelectorAll('.prod-row');
|
|
let visible = 0;
|
|
|
|
filas.forEach(fila => {
|
|
const mostrar = cat === 'todos' || fila.dataset.cat === cat;
|
|
fila.style.display = mostrar ? '' : 'none';
|
|
if (mostrar) visible++;
|
|
});
|
|
|
|
document.getElementById('cat-count-num').textContent = visible;
|
|
document.getElementById('no-results').style.display = visible === 0 ? 'block' : 'none';
|
|
}
|
|
</script>
|
|
|
|
</body>
|
|
</html>
|