Autosave: 20260801-192531

This commit is contained in:
Flatlogic Bot 2026-08-01 19:23:53 +00:00
parent d1def919d5
commit ec0a3af33e
4 changed files with 174 additions and 32 deletions

View File

@ -1796,7 +1796,7 @@ require_once 'layout_header.php';
</div>
</div>
<div class="d-flex flex-column gap-2">
<button type="button" class="btn btn-outline-primary btn-sm" id="airDroidCopyButton-<?php echo htmlspecialchars($order['source_key']); ?>" data-phone="<?php echo $phoneHiddenByDay1 ? '' : htmlspecialchars((string) ($order['celular'] ?? '')); ?>" onclick="copyPhoneToClipboard(this.dataset.phone); return false;"<?php echo $phoneHiddenByDay1 ? ' disabled' : ''; ?>><?php echo $phoneHiddenByDay1 ? 'Número oculto' : 'Copiar número'; ?></button>
<button type="button" class="btn btn-outline-primary btn-sm" id="airDroidCopyButton-<?php echo htmlspecialchars($order['source_key']); ?>" data-phone="<?php echo $phoneHiddenByDay1 ? '' : htmlspecialchars((string) ($order['celular'] ?? '')); ?>" onclick="copyPhoneButtonAction(this); return false;"<?php echo $phoneHiddenByDay1 ? ' disabled' : ''; ?>><?php echo $phoneHiddenByDay1 ? 'Número oculto' : 'Copiar número'; ?></button>
<button type="button" class="btn btn-outline-secondary btn-sm" onclick="openAirDroidWeb()">Abrir AirDroid Web</button>
</div>
</div>
@ -2771,6 +2771,50 @@ function copyTextLegacy(text) {
return copied;
}
function flashCopyButtonFeedback(button) {
if (!button) {
return;
}
const defaultLabel = button.dataset.defaultCopyLabel || button.textContent || 'Copiar número';
button.dataset.defaultCopyLabel = defaultLabel;
if (button._copyFeedbackTimer) {
window.clearTimeout(button._copyFeedbackTimer);
button._copyFeedbackTimer = null;
}
button.textContent = 'Copiado ✅';
button.setAttribute('aria-live', 'polite');
button._copyFeedbackTimer = window.setTimeout(() => {
if (button.textContent === 'Copiado ✅') {
button.textContent = defaultLabel;
}
button.removeAttribute('aria-live');
button._copyFeedbackTimer = null;
}, 1300);
}
function copyPhoneButtonAction(button) {
if (!button) {
return Promise.resolve(false);
}
const phone = button.dataset.phone || '';
return copyPhoneToClipboard(phone).then(copied => {
if (copied) {
flashCopyButtonFeedback(button);
} else {
alert('No se pudo copiar el número automáticamente.');
}
return copied;
}).catch(() => {
alert('No se pudo copiar el número automáticamente.');
return false;
});
}
function copyPhoneToClipboard(phone) {
if (!phone) {
return Promise.resolve(false);
@ -2821,24 +2865,26 @@ function showAirDroidHelper(details) {
phoneNode.textContent = phone !== '-' ? phone : 'Sin celular';
}
}
if (copyButton) {
if (copyButton) {
if (phoneHiddenByDay1) {
copyButton.dataset.phone = '';
copyButton.disabled = true;
copyButton.textContent = 'Número oculto';
copyButton.dataset.defaultCopyLabel = 'Número oculto';
copyButton.removeAttribute('aria-live');
copyButton.setAttribute('title', 'Número oculto hasta completar Promo Final');
} else {
copyButton.dataset.phone = phone !== '-' ? phone : '';
copyButton.disabled = false;
copyButton.textContent = 'Copiar número';
copyButton.dataset.defaultCopyLabel = 'Copiar número';
copyButton.removeAttribute('aria-live');
copyButton.removeAttribute('title');
}
}
openButton.onclick = function () {
openAirDroidWeb();
};
}
}
function registrarLlamada(event, trigger) {
@ -2934,12 +2980,7 @@ function registrarLlamada(event, trigger) {
const airDroidCopyButton = document.getElementById('airDroidCopyButton');
if (airDroidCopyButton) {
airDroidCopyButton.addEventListener('click', function () {
const phone = this.dataset.phone || '';
copyPhoneToClipboard(phone).then(copied => {
if (!copied) {
alert('No se pudo copiar el número automáticamente.');
}
});
copyPhoneButtonAction(this);
});
}

View File

@ -2285,7 +2285,7 @@ require_once 'layout_header.php';
</div>
</div>
<div class="d-flex flex-column gap-2">
<button type="button" class="btn btn-outline-primary btn-sm" id="airDroidCopyButton-<?php echo htmlspecialchars($order['source_key']); ?>" data-phone="<?php echo $phoneHiddenByDay1 ? '' : htmlspecialchars((string) ($order['celular'] ?? '')); ?>" onclick="copyPhoneToClipboard(this.dataset.phone); return false;"<?php echo $phoneHiddenByDay1 ? ' disabled' : ''; ?>><?php echo $phoneHiddenByDay1 ? 'Número oculto' : 'Copiar número'; ?></button>
<button type="button" class="btn btn-outline-primary btn-sm" id="airDroidCopyButton-<?php echo htmlspecialchars($order['source_key']); ?>" data-phone="<?php echo $phoneHiddenByDay1 ? '' : htmlspecialchars((string) ($order['celular'] ?? '')); ?>" onclick="copyPhoneButtonAction(this); return false;"<?php echo $phoneHiddenByDay1 ? ' disabled' : ''; ?>><?php echo $phoneHiddenByDay1 ? 'Número oculto' : 'Copiar número'; ?></button>
<button type="button" class="btn btn-outline-secondary btn-sm" onclick="openAirDroidWeb()">Abrir AirDroid Web</button>
</div>
</div>
@ -3306,6 +3306,50 @@ function copyTextLegacy(text) {
return copied;
}
function flashCopyButtonFeedback(button) {
if (!button) {
return;
}
const defaultLabel = button.dataset.defaultCopyLabel || button.textContent || 'Copiar número';
button.dataset.defaultCopyLabel = defaultLabel;
if (button._copyFeedbackTimer) {
window.clearTimeout(button._copyFeedbackTimer);
button._copyFeedbackTimer = null;
}
button.textContent = 'Copiado ✅';
button.setAttribute('aria-live', 'polite');
button._copyFeedbackTimer = window.setTimeout(() => {
if (button.textContent === 'Copiado ✅') {
button.textContent = defaultLabel;
}
button.removeAttribute('aria-live');
button._copyFeedbackTimer = null;
}, 1300);
}
function copyPhoneButtonAction(button) {
if (!button) {
return Promise.resolve(false);
}
const phone = button.dataset.phone || '';
return copyPhoneToClipboard(phone).then(copied => {
if (copied) {
flashCopyButtonFeedback(button);
} else {
alert('No se pudo copiar el número automáticamente.');
}
return copied;
}).catch(() => {
alert('No se pudo copiar el número automáticamente.');
return false;
});
}
function copyPhoneToClipboard(phone) {
if (!phone) {
return Promise.resolve(false);
@ -3356,24 +3400,26 @@ function showAirDroidHelper(details) {
phoneNode.textContent = phone !== '-' ? phone : 'Sin celular';
}
}
if (copyButton) {
if (copyButton) {
if (phoneHiddenByDay1) {
copyButton.dataset.phone = '';
copyButton.disabled = true;
copyButton.textContent = 'Número oculto';
copyButton.dataset.defaultCopyLabel = 'Número oculto';
copyButton.removeAttribute('aria-live');
copyButton.setAttribute('title', 'Número oculto hasta completar Promo Final');
} else {
copyButton.dataset.phone = phone !== '-' ? phone : '';
copyButton.disabled = false;
copyButton.textContent = 'Copiar número';
copyButton.dataset.defaultCopyLabel = 'Copiar número';
copyButton.removeAttribute('aria-live');
copyButton.removeAttribute('title');
}
}
openButton.onclick = function () {
openAirDroidWeb();
};
}
}
function registrarLlamada(event, trigger) {
@ -3469,12 +3515,7 @@ function registrarLlamada(event, trigger) {
const airDroidCopyButton = document.getElementById('airDroidCopyButton');
if (airDroidCopyButton) {
airDroidCopyButton.addEventListener('click', function () {
const phone = this.dataset.phone || '';
copyPhoneToClipboard(phone).then(copied => {
if (!copied) {
alert('No se pudo copiar el número automáticamente.');
}
});
copyPhoneButtonAction(this);
});
}

View File

@ -1335,6 +1335,16 @@ function cc_test_followup_promo_final_cutoff(): DateTimeImmutable
return new DateTimeImmutable('2026-07-29 00:00:00');
}
function cc_test_followup_is_from_promo_final_cutoff(array $order): bool
{
$startedAt = cc_test_followup_started_at($order);
if (!$startedAt instanceof DateTimeImmutable) {
return false;
}
return $startedAt->format('Y-m-d') >= cc_test_followup_promo_final_cutoff()->format('Y-m-d');
}
function cc_test_followup_promo_final_threshold_day(array $order): int
{
$startedAt = cc_test_followup_started_at($order);
@ -1430,6 +1440,10 @@ function cc_test_followup_pending_promo_final_counts(array $orders): array
continue;
}
if (!cc_test_followup_is_from_promo_final_cutoff($order)) {
continue;
}
$ownerKey = cc_test_followup_owner_lock_key($order);
if ($ownerKey === '') {
continue;

View File

@ -1,6 +1,39 @@
<?php
session_start();
require_once 'db/config.php';
if (!function_exists('normalizeLoginIdentifier')) {
function normalizeLoginIdentifier(string $value): string
{
$value = trim($value);
return function_exists('mb_strtolower') ? mb_strtolower($value, 'UTF-8') : strtolower($value);
}
}
if (!function_exists('normalizeSessionRole')) {
function normalizeSessionRole(?string $role): string
{
$role = normalizeLoginIdentifier((string) ($role ?? ''));
$aliases = [
'administrador' => 'admin',
'admin' => 'admin',
'asesor' => 'asesor',
'marketing' => 'marketing',
'control logistico' => 'control_logistico',
'control_logistico' => 'control_logistico',
'soporte logistico' => 'soporte_logistico',
'soporte_logistico' => 'soporte_logistico',
'verificador de pagos' => 'verificador_pagos',
'verificador_pagos' => 'verificador_pagos',
'logistica' => 'logistica',
];
return $aliases[$role] ?? $role;
}
}
if (isset($_SESSION['user_id'])) {
$currentRole = (string) ($_SESSION['user_role'] ?? '');
if (in_array($currentRole, ['Administrador', 'admin'], true)) {
@ -15,28 +48,39 @@ if (isset($_SESSION['user_id'])) {
exit();
}
require_once 'db/config.php';
$error = '';
$submittedUsername = '';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$username = $_POST['username'];
$password = $_POST['password'];
$username = trim((string) ($_POST['username'] ?? ''));
$password = (string) ($_POST['password'] ?? '');
$submittedUsername = $username;
if (empty($username) || empty($password)) {
if ($username === '' || $password === '') {
$error = 'Please enter both username and password.';
} else {
try {
$db = db();
$stmt = $db->prepare('SELECT * FROM users WHERE username = :username');
$stmt->bindParam(':username', $username);
$lookup = normalizeLoginIdentifier($username);
$stmt = $db->prepare('SELECT * FROM users WHERE LOWER(TRIM(username)) = :identifier LIMIT 1');
$stmt->bindValue(':identifier', $lookup, PDO::PARAM_STR);
$stmt->execute();
$user = $stmt->fetch(PDO::FETCH_ASSOC);
if ($user && password_verify($password, $user['password'])) {
if (!$user) {
$stmt = $db->prepare("SELECT * FROM users WHERE LOWER(TRIM(COALESCE(nombre_asesor, ''))) = :identifier LIMIT 1");
$stmt->bindValue(':identifier', $lookup, PDO::PARAM_STR);
$stmt->execute();
$user = $stmt->fetch(PDO::FETCH_ASSOC);
}
if ($user && password_verify($password, (string) ($user['password'] ?? ''))) {
$_SESSION['user_id'] = $user['id'];
$_SESSION['username'] = $user['username'];
$_SESSION['user_role'] = $user['role'];
$_SESSION['username'] = trim((string) ($user['username'] ?? ''));
$_SESSION['user_role'] = (string) ($user['role'] ?? '');
$_SESSION['role'] = normalizeSessionRole((string) ($user['role'] ?? ''));
$_SESSION['nombre_asesor'] = trim((string) ($user['nombre_asesor'] ?? ''));
// Record session
try {
@ -75,8 +119,10 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
header('Location: pedidos.php');
}
exit();
} elseif ($user) {
$error = 'Invalid password.';
} else {
$error = 'Invalid username or password.';
$error = 'We could not find an account with that username or advisor name.';
}
} catch (PDOException $e) {
$error = 'Database error: ' . $e->getMessage();
@ -105,12 +151,12 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
<div class="card-body">
<h3 class="card-title text-center">Login</h3>
<?php if ($error): ?>
<div class="alert alert-danger"><?php echo $error; ?></div>
<div class="alert alert-danger"><?php echo htmlspecialchars($error); ?></div>
<?php endif; ?>
<form method="POST">
<div class="mb-3">
<label for="username" class="form-label">Username</label>
<input type="text" class="form-control" id="username" name="username" required>
<input type="text" class="form-control" id="username" name="username" value="<?php echo htmlspecialchars($submittedUsername); ?>" required>
</div>
<div class="mb-3">
<label for="password" class="form-label">Password</label>
@ -125,4 +171,4 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>
</html>