diff --git a/assets/uploads/vouchers/6a01e367e6894-Screenshot_317.png b/assets/uploads/vouchers/6a01e367e6894-Screenshot_317.png new file mode 100644 index 00000000..cb044a08 Binary files /dev/null and b/assets/uploads/vouchers/6a01e367e6894-Screenshot_317.png differ diff --git a/assets/uploads/vouchers/6a01e8201d446-Captura de pantalla 2026-05-11 093021.png b/assets/uploads/vouchers/6a01e8201d446-Captura de pantalla 2026-05-11 093021.png new file mode 100644 index 00000000..d7642e39 Binary files /dev/null and b/assets/uploads/vouchers/6a01e8201d446-Captura de pantalla 2026-05-11 093021.png differ diff --git a/assets/uploads/vouchers/6a0203449478a-Screenshot_318.png b/assets/uploads/vouchers/6a0203449478a-Screenshot_318.png new file mode 100644 index 00000000..624da645 Binary files /dev/null and b/assets/uploads/vouchers/6a0203449478a-Screenshot_318.png differ diff --git a/assets/uploads/vouchers/6a020419908fd-Screenshot_319.png b/assets/uploads/vouchers/6a020419908fd-Screenshot_319.png new file mode 100644 index 00000000..da53d50c Binary files /dev/null and b/assets/uploads/vouchers/6a020419908fd-Screenshot_319.png differ diff --git a/assets/uploads/vouchers/6a020b52cf146-Captura de pantalla 2026-05-11 115938.png b/assets/uploads/vouchers/6a020b52cf146-Captura de pantalla 2026-05-11 115938.png new file mode 100644 index 00000000..6eb52fd5 Binary files /dev/null and b/assets/uploads/vouchers/6a020b52cf146-Captura de pantalla 2026-05-11 115938.png differ diff --git a/assets/uploads/vouchers/6a0225938fe98-Screenshot_320.png b/assets/uploads/vouchers/6a0225938fe98-Screenshot_320.png new file mode 100644 index 00000000..f2d7ca31 Binary files /dev/null and b/assets/uploads/vouchers/6a0225938fe98-Screenshot_320.png differ diff --git a/assets/uploads/vouchers/6a024db09d697-Screenshot_321.png b/assets/uploads/vouchers/6a024db09d697-Screenshot_321.png new file mode 100644 index 00000000..e4199dcc Binary files /dev/null and b/assets/uploads/vouchers/6a024db09d697-Screenshot_321.png differ diff --git a/assets/uploads/vouchers/6a025c7460a90-Screenshot_322.png b/assets/uploads/vouchers/6a025c7460a90-Screenshot_322.png new file mode 100644 index 00000000..4e0b714f Binary files /dev/null and b/assets/uploads/vouchers/6a025c7460a90-Screenshot_322.png differ diff --git a/db/migrations/032_create_user_sessions_table.sql b/db/migrations/032_create_user_sessions_table.sql new file mode 100644 index 00000000..796f1ae3 --- /dev/null +++ b/db/migrations/032_create_user_sessions_table.sql @@ -0,0 +1,8 @@ +CREATE TABLE IF NOT EXISTS user_sessions ( + id INT AUTO_INCREMENT PRIMARY KEY, + user_id INT NOT NULL, + login_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + ip_address VARCHAR(45), + user_agent TEXT, + FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE +); diff --git a/db/migrations/033_add_city_to_user_sessions.sql b/db/migrations/033_add_city_to_user_sessions.sql new file mode 100644 index 00000000..2371ac44 --- /dev/null +++ b/db/migrations/033_add_city_to_user_sessions.sql @@ -0,0 +1 @@ +ALTER TABLE user_sessions ADD COLUMN ciudad VARCHAR(100) AFTER ip_address; diff --git a/ingreso_mercaderia.php b/ingreso_mercaderia.php index 1166cd6a..82631f3d 100644 --- a/ingreso_mercaderia.php +++ b/ingreso_mercaderia.php @@ -65,7 +65,9 @@ $ingresos = $stmt->fetchAll(PDO::FETCH_ASSOC); elseif ($porcentaje > 0) $barColor = 'bg-primary'; ?> - + + + diff --git a/layout_header.php b/layout_header.php index 759aac80..a000c539 100644 --- a/layout_header.php +++ b/layout_header.php @@ -251,11 +251,24 @@ $navItems = [ ], ] ], - 'configuracion' => [ - 'url' => 'configuracion.php', + 'configuracion_group' => [ 'icon' => 'fa-cog', 'text' => 'Configuración', - 'roles' => ['Administrador', 'admin'] + 'roles' => ['Administrador', 'admin'], + 'submenu' => [ + 'configuracion' => [ + 'url' => 'configuracion.php', + 'icon' => 'fa-cog', + 'text' => 'Configuración', + 'roles' => ['Administrador', 'admin'] + ], + 'sesiones_iniciadas' => [ + 'url' => 'sesiones_iniciadas.php', + 'icon' => 'fa-user-shield', + 'text' => 'Sesiones Iniciadas', + 'roles' => ['Administrador', 'admin'] + ] + ] ] ]; diff --git a/login.php b/login.php index 596e822f..03204d3d 100644 --- a/login.php +++ b/login.php @@ -28,6 +28,33 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { $_SESSION['user_id'] = $user['id']; $_SESSION['username'] = $user['username']; $_SESSION['user_role'] = $user['role']; + + // Record session + try { + $ip = $_SERVER['REMOTE_ADDR'] ?? 'unknown'; + $ua = $_SERVER['HTTP_USER_AGENT'] ?? 'unknown'; + $ciudad = 'Desconocida'; + + if ($ip !== 'unknown' && $ip !== '127.0.0.1' && $ip !== '::1') { + $geo = @file_get_contents("http://ip-api.com/json/{$ip}?fields=city"); + if ($geo) { + $geoData = json_decode($geo, true); + if (isset($geoData['city'])) { + $ciudad = $geoData['city']; + } + } + } + + $stmtSession = $db->prepare('INSERT INTO user_sessions (user_id, ip_address, ciudad, user_agent) VALUES (:user_id, :ip, :ciudad, :ua)'); + $stmtSession->bindParam(':user_id', $user['id']); + $stmtSession->bindParam(':ip', $ip); + $stmtSession->bindParam(':ciudad', $ciudad); + $stmtSession->bindParam(':ua', $ua); + $stmtSession->execute(); + } catch (PDOException $e) { + // Silently fail if session recording fails, don't block login + } + header('Location: pedidos.php'); exit(); } else { diff --git a/pedidos_en_transito.php b/pedidos_en_transito.php index 87d196e0..f0b2bb04 100644 --- a/pedidos_en_transito.php +++ b/pedidos_en_transito.php @@ -237,11 +237,6 @@ include 'layout_header.php'; " . "Quedamos atentos a su confirmación. -" - . "Muchas gracias por su confianza 🤝 - -" - . "Floower Store 🛍️ " . "Área de Atención al Cliente"; @@ -254,18 +249,18 @@ include 'layout_header.php'; '{SALDO_PENDIENTE}' => number_format($monto_debe, 2) ]; - $whatsappMessage = str_replace(array_keys($replacements), array_values($replacements), $template); - $whatsappMessage = urlencode($whatsappMessage); + $whatsappMessageRaw = str_replace(array_keys($replacements), array_values($replacements), $template); + $whatsappMessageEncoded = urlencode($whatsappMessageRaw); $celular = preg_replace('/[^0-9]/', '', $pedido['celular']); if (strlen($celular) == 9) { $celular = '51' . $celular; } - $whatsappUrl = "https://api.whatsapp.com/send?phone={$celular}&text={$whatsappMessage}"; + $whatsappUrl = "https://api.whatsapp.com/send?phone={$celular}&text={$whatsappMessageEncoded}"; ?>
- + 🚚 💬
@@ -347,6 +342,7 @@ document.addEventListener('DOMContentLoaded', function() { var button = event.relatedTarget; var orderNumber = button.getAttribute('data-order-number'); var orderCode = button.getAttribute('data-order-code'); + var whatsappMessage = button.getAttribute('data-whatsapp-message'); var modalOrderNumberSpan = trackingModal.querySelector('#modal-order-number'); var modalOrderCodeSpan = trackingModal.querySelector('#modal-order-code'); @@ -508,13 +504,17 @@ document.addEventListener('DOMContentLoaded', function() {
-
`; modalStatusDiv.innerHTML = html; + + document.getElementById('btn-copy-summary').addEventListener('click', function() { + copyStatusToClipboard(whatsappMessage); + }); } else { modalStatusDiv.innerHTML = `

No se pudo encontrar la guía.

`; } @@ -614,14 +614,8 @@ document.addEventListener('DOMContentLoaded', function() { } }); -function copyStatusToClipboard(status, order, destination) { - const text = `📦 *Estado de tu pedido Shalom*\n\n` + - `🔖 *Nro de Orden:* ${order}\n` + - `📍 *Destino:* ${destination}\n` + - `🚚 *Estado:* ${status}\n\n` + - `¡Tu pedido está en camino! Gracias por tu confianza. ✨`; - - navigator.clipboard.writeText(text).then(() => { +function copyStatusToClipboard(message) { + navigator.clipboard.writeText(message).then(() => { alert('Resumen copiado al portapapeles. Ya puedes pegarlo en WhatsApp.'); }).catch(err => { console.error('Error al copiar:', err); diff --git a/sesiones_iniciadas.php b/sesiones_iniciadas.php new file mode 100644 index 00000000..dc981ad5 --- /dev/null +++ b/sesiones_iniciadas.php @@ -0,0 +1,86 @@ +query(" + SELECT s.*, u.username, u.nombre_asesor + FROM user_sessions s + JOIN users u ON s.user_id = u.id + ORDER BY s.login_time DESC + LIMIT 500 +"); +$sessions = $stmt->fetchAll(PDO::FETCH_ASSOC); + +include 'layout_header.php'; +?> + +
+
+
+
Historial de Inicios de Sesión (Últimos 500)
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
UsuarioNombre/AsesorFecha y HoraDirección IPCiudadNavegador / Dispositivo
No hay registros de sesiones aún.
+
+
+
+
+ + + + + + \ No newline at end of file