Autosave: 20260512-105548

This commit is contained in:
Flatlogic Bot 2026-05-12 10:55:49 +00:00
parent 77cd3da5bc
commit 667d0238fa
15 changed files with 153 additions and 22 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 246 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 190 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 234 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 232 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 141 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 235 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 351 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 473 KiB

View File

@ -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
);

View File

@ -0,0 +1 @@
ALTER TABLE user_sessions ADD COLUMN ciudad VARCHAR(100) AFTER ip_address;

View File

@ -65,7 +65,9 @@ $ingresos = $stmt->fetchAll(PDO::FETCH_ASSOC);
elseif ($porcentaje > 0) $barColor = 'bg-primary';
?>
<tr>
<td><?php echo date('d/m/Y H:i', strtotime($ingreso['fecha_registro'])); ?></td>
<td data-order="<?php echo strtotime($ingreso['fecha_registro']); ?>">
<?php echo date('d/m/Y H:i', strtotime($ingreso['fecha_registro'])); ?>
</td>
<td>
<strong><?php echo htmlspecialchars($ingreso['nombre_producto']); ?></strong>
<?php if ($ingreso['product_id']): ?>

View File

@ -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']
]
]
]
];

View File

@ -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 {

View File

@ -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}";
?>
<div class="mt-1">
<button type="button" class="btn btn-sm btn-info" title="Consultar Estado" data-bs-toggle="modal" data-bs-target="#trackingModal" data-order-number="<?php echo htmlspecialchars($pedido['codigo_rastreo'] ?? 'N/A'); ?>" data-order-code="<?php echo htmlspecialchars($pedido['codigo_tracking'] ?? 'N/A'); ?>">🔍</button>
<button type="button" class="btn btn-sm btn-info" title="Consultar Estado" data-bs-toggle="modal" data-bs-target="#trackingModal" data-order-number="<?php echo htmlspecialchars($pedido['codigo_rastreo'] ?? 'N/A'); ?>" data-order-code="<?php echo htmlspecialchars($pedido['codigo_tracking'] ?? 'N/A'); ?>" data-whatsapp-message="<?php echo htmlspecialchars($whatsappMessageRaw); ?>">🔍</button>
<a href="https://rastrea.shalom.pe/rastrea" target="_blank" class="btn btn-sm btn-primary" title="Rastreo Shalom">🚚</a>
<a href="<?php echo $whatsappUrl; ?>" target="_blank" class="btn btn-sm btn-secondary whatsapp-icon" id="whatsapp-icon-<?php echo $pedido['id']; ?>" title="Enviar WhatsApp">💬</a>
</div>
@ -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() {
</div>
</div>
<div class="d-grid gap-2 mt-4">
<button class="btn btn-success btn-lg" onclick="copyStatusToClipboard('${statusMessage}', '${orderNumber}', '${searchData.destino.nombre}')">
<button class="btn btn-success btn-lg" id="btn-copy-summary">
<i class="fab fa-whatsapp"></i> Copiar Resumen para Cliente
</button>
</div>
`;
modalStatusDiv.innerHTML = html;
document.getElementById('btn-copy-summary').addEventListener('click', function() {
copyStatusToClipboard(whatsappMessage);
});
} else {
modalStatusDiv.innerHTML = `<p class="text-warning text-center">No se pudo encontrar la guía.</p>`;
}
@ -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);

86
sesiones_iniciadas.php Normal file
View File

@ -0,0 +1,86 @@
<?php
session_start();
if (!isset($_SESSION['user_id'])) {
header('Location: login.php');
exit;
}
require_once 'db/config.php';
$user_role = $_SESSION['user_role'];
if ($user_role !== 'Administrador' && $user_role !== 'admin') {
header('Location: dashboard.php');
exit;
}
$pdo = db();
$pageTitle = "Sesiones Iniciadas";
// Obtener el historial de sesiones
$stmt = $pdo->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';
?>
<div class="container-fluid">
<div class="card shadow mb-4">
<div class="card-header py-3">
<h6 class="m-0 font-weight-bold text-primary">Historial de Inicios de Sesión (Últimos 500)</h6>
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table table-bordered" id="sessionsTable" width="100%" cellspacing="0">
<thead>
<tr>
<th>Usuario</th>
<th>Nombre/Asesor</th>
<th>Fecha y Hora</th>
<th>Dirección IP</th>
<th>Ciudad</th>
<th>Navegador / Dispositivo</th>
</tr>
</thead>
<tbody>
<?php foreach ($sessions as $session): ?>
<tr>
<td><?php echo htmlspecialchars($session['username']); ?></td>
<td><?php echo htmlspecialchars($session['nombre_asesor'] ?? 'N/A'); ?></td>
<td><?php echo date('d/m/Y H:i:s', strtotime($session['login_time'])); ?></td>
<td><?php echo htmlspecialchars($session['ip_address']); ?></td>
<td><span class="badge bg-info text-dark"><?php echo htmlspecialchars($session['ciudad'] ?? 'N/A'); ?></span></td>
<td style="font-size: 0.85rem;"><?php echo htmlspecialchars($session['user_agent']); ?></td>
</tr>
<?php endforeach; ?>
<?php if (empty($sessions)): ?>
<tr>
<td colspan="6" class="text-center">No hay registros de sesiones aún.</td>
</tr>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
</div>
</div>
<script src="https://cdn.datatables.net/1.13.6/js/jquery.dataTables.min.js"></script>
<script src="https://cdn.datatables.net/1.13.6/js/dataTables.bootstrap5.min.js"></script>
<script>
$(document).ready(function() {
$('#sessionsTable').DataTable({
"order": [[ 2, "desc" ]],
"language": {
"url": "//cdn.datatables.net/plug-ins/1.13.6/i18n/es-ES.json"
}
});
});
</script>
<?php include 'layout_footer.php'; ?>