panel admin: gestion de pedidos

This commit is contained in:
Brisa
2026-06-30 14:59:19 -03:00
parent 47ef868129
commit c0af5c08ae
9 changed files with 1496 additions and 0 deletions
+526
View File
@@ -0,0 +1,526 @@
<?php
include("../db.php");
if (!isset($_SESSION['rol']) || $_SESSION['rol'] !== 'admin') {
header("Location: ../login.php?error=acceso_denegado");
exit();
}
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['cambiar_pago'])) {
$id_p = (int)$_POST['id_pedido'];
$nuevo_pago = mysqli_real_escape_string($conexion, $_POST['nuevo_pago']);
$query_pago = "UPDATE pedidos SET pago_estado = '$nuevo_pago' WHERE id_pedido = $id_p";
if (mysqli_query($conexion, $query_pago)) {
$nombre_admin = $_SESSION['usuario'] ?? 'Admin ID: ' . ($_SESSION['id_usuario'] ?? 'Desconocido');
escribir_log("[CAJA] Pedido #$id_p actualizó su estado de pago a '$nuevo_pago' por el Administrador: $nombre_admin");
header("Location: pedidos.php?status=updated");
} else {
escribir_log("[ERROR SQL] Fallo al intentar cambiar pago del pedido #$id_p. Error: " . mysqli_error($conexion));
header("Location: pedidos.php?status=error");
}
exit();
}
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['cambiar_estado'])) {
$id_p = (int)$_POST['id_pedido'];
$nuevo_est = mysqli_real_escape_string($conexion, $_POST['nuevo_estado']);
$query_cliente = "SELECT p.telefono_contacto, u.nombre, p.total
FROM pedidos p
JOIN usuarios u ON p.id_usuario = u.id_usuario
WHERE p.id_pedido = $id_p";
$res_c = mysqli_query($conexion, $query_cliente);
$data_c = mysqli_fetch_assoc($res_c);
$query_update = "UPDATE pedidos SET estado = '$nuevo_est' WHERE id_pedido = $id_p";
if (mysqli_query($conexion, $query_update)) {
$nombre_admin = $_SESSION['usuario'] ?? 'Admin ID: ' . ($_SESSION['id_usuario'] ?? 'Desconocido');
$nombre_cliente = $data_c['nombre'] ?? 'Desconocido';
if ($nuevo_est === 'Cancelado') {
$motivo = $_POST['motivo_cancelacion'] ?? 'No especificado o directo desde monitor';
escribir_log("[🚨 CANCELACIÓN] Pedido ID #$id_p fue CANCELADO por Admin: $nombre_admin. Motivo: $motivo");
} else {
escribir_log("[CAJA/COCINA] Pedido #$id_p (Cliente: $nombre_cliente) cambio a estado '$nuevo_est' por el Administrador: $nombre_admin");
}
if ($nuevo_est !== 'Cancelado' && !empty($data_c['telefono_contacto']) && ($nuevo_est === 'En camino' || $nuevo_est === 'Listo para retirar')) {
if ($nuevo_est === 'En camino') {
$texto = "¡Hola " . $data_c['nombre'] . "! Tu pedido de *La Pecosa* ya está en camino. 🛵 ¡Prepárate para recibirlo! Total a pagar: $" . number_format($data_c['total'], 2, ',', '.');
} else {
$texto = "¡Hola " . $data_c['nombre'] . "! Tu pedido de *La Pecosa* ya está listo para ser retirado. 🛍️ ¡Te esperamos! Total: $" . number_format($data_c['total'], 2, ',', '.');
}
$mensaje_ws = urlencode($texto);
$telefono_limpio = preg_replace('/[^0-9]/', '', $data_c['telefono_contacto']);
if (substr($telefono_limpio, 0, 2) !== '54') {
$telefono_limpio = '54' . $telefono_limpio;
}
$_SESSION['url_whatsapp'] = "https://api.whatsapp.com/send?phone=$telefono_limpio&text=$mensaje_ws";
}
header("Location: pedidos.php?status=updated");
} else {
escribir_log("[ERROR SQL] Fallo al intentar cambiar el estado del pedido #$id_p a '$nuevo_est'. Error: " . mysqli_error($conexion));
header("Location: pedidos.php?status=error");
}
exit();
}
$query_activos = "SELECT p.id_pedido, p.fecha, p.total, p.estado, p.pago_estado, p.comprobante, u.nombre AS cliente
FROM pedidos p
JOIN usuarios u ON p.id_usuario = u.id_usuario
WHERE p.estado IN ('Pendiente', 'En cocina', 'Listo para retirar', 'En camino')
ORDER BY p.fecha DESC";
$res_activos = mysqli_query($conexion, $query_activos);
$query_historial = "SELECT p.id_pedido, p.fecha, p.total, p.estado, p.pago_estado, p.comprobante, u.nombre AS cliente
FROM pedidos p
JOIN usuarios u ON p.id_usuario = u.id_usuario
WHERE p.estado IN ('Entregado', 'Cancelado')
ORDER BY p.fecha DESC
LIMIT 50";
$res_historial = mysqli_query($conexion, $query_historial);
?>
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Pedidos Activos - La Pecosa</title>
<link rel="stylesheet" href="../css/all.min.css">
<link rel="stylesheet" href="admin_style.css">
<style>
/* ── TOAST ── */
.toast-ok {
background: #f0fdf4;
border: 1px solid #bbf7d0;
color: #166534;
border-radius: 8px;
padding: 12px 16px;
margin-bottom: 20px;
font-size: 0.875rem;
font-weight: 600;
display: flex;
align-items: center;
gap: 8px;
}
/* ── BUSCADOR ── */
.search-bar {
background: #fff;
border: 1px solid #e5e7eb;
border-radius: 10px;
padding: 16px 20px;
margin-bottom: 24px;
display: flex;
gap: 10px;
align-items: center;
}
.search-bar .search-wrap {
position: relative;
flex: 1;
}
.search-bar .search-wrap i {
position: absolute;
left: 14px;
top: 50%;
transform: translateY(-50%);
color: #9ca3af;
font-size: 0.9rem;
}
.search-bar input {
width: 100%;
padding: 10px 12px 10px 40px;
border: 1px solid #e5e7eb;
border-radius: 7px;
font-size: 0.9rem;
outline: none;
transition: border-color 0.2s;
box-sizing: border-box;
}
.search-bar input:focus { border-color: #e02828; }
.search-bar .btn-buscar {
background: #e02828;
color: #fff;
border: none;
padding: 10px 20px;
border-radius: 7px;
font-weight: 700;
font-size: 0.875rem;
cursor: pointer;
display: flex;
align-items: center;
gap: 6px;
transition: opacity 0.15s;
}
.search-bar .btn-buscar:hover { opacity: 0.88; }
/* ── SECTION CARD ── */
.section-card {
background: #fff;
border: 1px solid #e5e7eb;
border-radius: 12px;
overflow: hidden;
margin-bottom: 20px;
}
.section-card-header {
padding: 16px 22px;
border-bottom: 1px solid #f3f4f6;
display: flex;
align-items: center;
justify-content: space-between;
}
.section-card-header h2 {
font-size: 1rem;
font-weight: 700;
color: #111827;
display: flex;
align-items: center;
gap: 8px;
margin: 0;
}
.section-card-body { overflow-x: auto; }
/* ── PULSO ── */
@keyframes pulse {
0% { opacity: 0.6; transform: scale(0.9); }
50% { opacity: 1; transform: scale(1.1); }
100% { opacity: 0.6; transform: scale(0.9); }
}
.dot-live {
width: 9px;
height: 9px;
border-radius: 50%;
background: #e02828;
display: inline-block;
animation: pulse 1.6s infinite;
}
/* ── TABLA ── */
.admin-table td { font-size: 0.875rem; vertical-align: middle; }
.admin-table tr:last-child td { border-bottom: none; }
/* ── CELDA ESTADO+PAGO (columna "Estado actual") ── */
.estado-cell { display: flex; flex-direction: column; gap: 5px; }
/* ── PAGO ── */
.pago-ok {
display: inline-flex;
align-items: center;
gap: 4px;
font-size: 0.75rem;
font-weight: 700;
color: #166534;
}
.pago-pendiente-btn {
display: inline-flex;
align-items: center;
gap: 4px;
font-size: 0.75rem;
font-weight: 700;
color: #9d174d;
background: #fdf2f8;
border: 1px dashed #fbcfe8;
border-radius: 5px;
padding: 3px 8px;
cursor: pointer;
transition: background 0.15s;
}
.pago-pendiente-btn:hover { background: #fce7f3; }
.pago-pendiente-tag {
display: inline-flex;
align-items: center;
gap: 4px;
font-size: 0.75rem;
font-weight: 700;
color: #9d174d;
background: #fdf2f8;
border-radius: 5px;
padding: 3px 8px;
}
/* ── COMPROBANTE ── */
.btn-comprobante {
display: inline-flex;
align-items: center;
gap: 4px;
font-size: 0.72rem;
font-weight: 600;
color: #475569;
background: #f8fafc;
border: 1px solid #e2e8f0;
border-radius: 5px;
padding: 3px 8px;
text-decoration: none;
transition: border-color 0.15s, color 0.15s;
}
.btn-comprobante:hover { border-color: #e02828; color: #e02828; }
/* ── VER DETALLE ── */
.btn-detalle {
display: inline-flex;
align-items: center;
gap: 5px;
font-size: 0.8rem;
font-weight: 600;
color: #475569;
background: #f1f5f9;
border-radius: 20px;
padding: 6px 12px;
text-decoration: none;
transition: background 0.15s, color 0.15s;
}
.btn-detalle:hover { background: #e2e8f0; color: #111827; }
/* ── FORM ESTADO ── */
.form-estado { display: flex; align-items: center; gap: 6px; }
.btn-confirmar {
background: #1f2937;
color: #fff;
border: none;
width: 30px;
height: 30px;
border-radius: 6px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
font-size: 0.75rem;
flex-shrink: 0;
transition: background 0.15s;
}
.btn-confirmar:hover { background: #e02828; }
/* ── HISTORIAL COLAPSABLE ── */
.historial-toggle {
width: 100%;
background: #f9fafb;
border: 1px solid #e5e7eb;
border-radius: 10px;
padding: 14px 22px;
display: flex;
align-items: center;
justify-content: space-between;
cursor: pointer;
font-size: 0.9rem;
font-weight: 700;
color: #374151;
transition: background 0.15s;
margin-bottom: 0;
}
.historial-toggle:hover { background: #f3f4f6; }
.historial-toggle .toggle-left { display: flex; align-items: center; gap: 8px; }
.historial-toggle .chevron {
font-size: 0.8rem;
color: #9ca3af;
transition: transform 0.25s;
}
.historial-toggle.open .chevron { transform: rotate(180deg); }
.historial-body {
display: none;
margin-top: 8px;
}
.historial-body.open { display: block; }
</style>
</head>
<body>
<?php include("sidebar.php"); ?>
<div class="main-content">
<div class="page-header">
<h1><i class="fa-solid fa-fire-burner"></i> Monitor operativo de pedidos</h1>
</div>
<?php if (isset($_GET['status']) && $_GET['status'] === 'updated'): ?>
<div class="toast-ok">
<i class="fa-solid fa-circle-check"></i> Panel actualizado correctamente.
</div>
<?php endif; ?>
<!-- BUSCADOR -->
<div class="search-bar">
<form action="buscar_clientes.php" method="GET" style="display:contents;">
<div class="search-wrap">
<i class="fa-solid fa-magnifying-glass"></i>
<input type="text" name="query" required placeholder="Buscar por #ID de pedido, nombre de cliente o ID de usuario...">
</div>
<button type="submit" class="btn-buscar">
<i class="fa-solid fa-filter"></i> Buscar
</button>
</form>
</div>
<!-- COMANDAS ACTIVAS -->
<div class="section-card">
<div class="section-card-header">
<h2><span class="dot-live"></span> Comandas activas — cocina y despacho</h2>
</div>
<div class="section-card-body">
<table class="admin-table">
<thead>
<tr>
<th>N° Pedido</th>
<th>Fecha / Hora</th>
<th>Cliente</th>
<th>Total</th>
<th>Estado actual</th>
<th>Ver detalle</th>
<th>Cambiar estado</th>
</tr>
</thead>
<tbody>
<?php if (mysqli_num_rows($res_activos) == 0): ?>
<tr>
<td colspan="7" style="text-align:center; color:#9ca3af; padding:36px 14px;">
<i class="fa-solid fa-mug-hot" style="font-size:1.4rem; display:block; margin-bottom:6px;"></i>
No hay pedidos activos en este momento.
</td>
</tr>
<?php else: ?>
<?php while ($ped = mysqli_fetch_assoc($res_activos)):
$en = strtolower($ped['estado']);
$badge = 'badge-pendiente';
if ($en === 'en cocina') $badge = 'badge-cocina';
if ($en === 'listo para retirar') $badge = 'badge-listo';
if ($en === 'en camino') $badge = 'badge-camino';
?>
<tr>
<td><strong>#<?php echo $ped['id_pedido']; ?></strong></td>
<td style="color:#6b7280;"><?php echo date('d/m/Y H:i', strtotime($ped['fecha'])); ?> hs</td>
<td><?php echo htmlspecialchars($ped['cliente']); ?></td>
<td><strong>$<?php echo number_format($ped['total'], 2, ',', '.'); ?></strong></td>
<td>
<div class="estado-cell">
<span class="badge <?php echo $badge; ?>"><?php echo htmlspecialchars($ped['estado']); ?></span>
<?php if ($ped['pago_estado'] === 'Aprobado'): ?>
<span class="pago-ok"><i class="fa-solid fa-circle-dollar-to-slot"></i> Pago aprobado</span>
<?php else: ?>
<form action="pedidos.php" method="POST" style="display:inline;">
<input type="hidden" name="id_pedido" value="<?php echo $ped['id_pedido']; ?>">
<input type="hidden" name="nuevo_pago" value="Aprobado">
<button type="submit" name="cambiar_pago" class="pago-pendiente-btn" title="Clic para registrar pago recibido">
<i class="fa-solid fa-hand-holding-dollar"></i> Pago pendiente
</button>
</form>
<?php endif; ?>
<?php if (!empty($ped['comprobante'])): ?>
<a href="imprimir_ticket.php?file=<?php echo urlencode($ped['comprobante']); ?>" target="_blank" class="btn-comprobante">
<i class="fa-solid fa-receipt"></i> Ver comprobante
</a>
<?php endif; ?>
</div>
</td>
<td>
<a href="ver_detalle.php?id=<?php echo $ped['id_pedido']; ?>&desde=pedidos" class="btn-detalle">
<i class="fa-solid fa-list-ul"></i> Ver detalle
</a>
</td>
<td>
<form action="pedidos.php" method="POST" class="form-estado">
<input type="hidden" name="id_pedido" value="<?php echo $ped['id_pedido']; ?>">
<input type="hidden" name="cambiar_estado" value="1">
<select name="nuevo_estado" class="select-status" required>
<option value="Pendiente" <?php echo $ped['estado'] === 'Pendiente' ? 'selected' : ''; ?>>Pendiente</option>
<option value="En cocina" <?php echo $ped['estado'] === 'En cocina' ? 'selected' : ''; ?>>En cocina</option>
<option value="Listo para retirar" <?php echo $ped['estado'] === 'Listo para retirar' ? 'selected' : ''; ?>>Listo para retirar</option>
<option value="En camino" <?php echo $ped['estado'] === 'En camino' ? 'selected' : ''; ?>>En camino</option>
<option value="Entregado">Entregado (Finalizar)</option>
<option value="Cancelado">Cancelado</option>
</select>
<button type="submit" class="btn-confirmar" title="Confirmar cambio">
<i class="fa-solid fa-check"></i>
</button>
</form>
</td>
</tr>
<?php endwhile; ?>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
<!-- HISTORIAL COLAPSABLE -->
<button class="historial-toggle" onclick="toggleHistorial(this)">
<span class="toggle-left">
<i class="fa-solid fa-clock-rotate-left" style="color:#6b7280;"></i>
Historial de pedidos finalizados
</span>
<i class="fa-solid fa-chevron-down chevron"></i>
</button>
<div class="historial-body" id="historialBody">
<div class="section-card">
<div class="section-card-body">
<table class="admin-table">
<thead>
<tr>
<th>N° Pedido</th>
<th>Fecha / Hora</th>
<th>Cliente</th>
<th>Total cobrado</th>
<th>Resolución</th>
</tr>
</thead>
<tbody>
<?php if (mysqli_num_rows($res_historial) == 0): ?>
<tr>
<td colspan="5" style="text-align:center; color:#9ca3af; padding:36px 14px;">
<i class="fa-solid fa-box-open" style="font-size:1.4rem; display:block; margin-bottom:6px;"></i>
Todavía no hay pedidos finalizados.
</td>
</tr>
<?php else: ?>
<?php while ($hist = mysqli_fetch_assoc($res_historial)):
$badge_h = ($hist['estado'] === 'Entregado') ? 'badge-entregado' : 'badge-cancelado';
?>
<tr>
<td><strong>#<?php echo $hist['id_pedido']; ?></strong></td>
<td style="color:#6b7280;"><?php echo date('d/m/Y H:i', strtotime($hist['fecha'])); ?> hs</td>
<td><?php echo htmlspecialchars($hist['cliente']); ?></td>
<td>$<?php echo number_format($hist['total'], 2, ',', '.'); ?></td>
<td>
<div class="estado-cell">
<span class="badge <?php echo $badge_h; ?>"><?php echo htmlspecialchars($hist['estado']); ?></span>
<?php if ($hist['pago_estado'] === 'Aprobado'): ?>
<span class="pago-ok"><i class="fa-solid fa-circle-dollar-to-slot"></i> Pago aprobado</span>
<?php else: ?>
<span class="pago-pendiente-tag"><i class="fa-solid fa-hand-holding-dollar"></i> Pago pendiente</span>
<?php endif; ?>
<?php if (!empty($hist['comprobante'])): ?>
<a href="imprimir_ticket.php?file=<?php echo urlencode($hist['comprobante']); ?>" target="_blank" class="btn-comprobante">
<i class="fa-solid fa-receipt"></i> Ver comprobante
</a>
<?php endif; ?>
</div>
</td>
</tr>
<?php endwhile; ?>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
</div>
</div>
<script>
function toggleHistorial(btn) {
btn.classList.toggle('open');
document.getElementById('historialBody').classList.toggle('open');
}
</script>
<?php if (isset($_SESSION['url_whatsapp'])): ?>
<script>
window.open("<?php echo $_SESSION['url_whatsapp']; ?>", "_blank");
</script>
<?php unset($_SESSION['url_whatsapp']); ?>
<?php endif; ?>
</body>
</html>