cotizador/pages/login.php

559 lines
28 KiB
PHP

<?php
// Incluir configuración de monedas
require_once __DIR__ . '/../config/currencies.php';
$currencies = getCurrenciesFromEnv();
?>
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Comparador de Precios - Login ..</title>
<link rel="icon" type="image/x-icon" href="/favicon.ico">
<link rel="stylesheet" href="/assets/css/tailwind.css">
</head>
<body class="bg-gradient-to-br from-blue-50 to-indigo-100 min-h-screen">
<!-- Botón Cotizar (Fixed en la esquina superior derecha) -->
<button id="cotizarBtn" class="fixed top-4 right-4 bg-green-600 hover:bg-green-700 text-white px-6 py-3 rounded-lg font-semibold shadow-lg transition-all flex items-center space-x-2 z-50">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8c-1.657 0-3 .895-3 2s1.343 2 3 2 3 .895 3 2-1.343 2-3 2m0-8c1.11 0 2.08.402 2.599 1M12 8V7m0 1v8m0 0v1m0-1c-1.11 0-2.08-.402-2.599-1M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
<span>Cotizar</span>
</button>
<!-- Modal de Cotización -->
<div id="cotizarModal" class="hidden fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center px-4 z-50">
<div class="bg-white rounded-2xl shadow-2xl max-w-md w-full p-6 relative">
<!-- Botón Cerrar -->
<button id="closeModalBtn" class="absolute top-4 right-4 text-gray-400 hover:text-gray-600 transition">
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
</svg>
</button>
<!-- Header -->
<div class="mb-6">
<h2 class="text-2xl font-bold text-gray-800 flex items-center space-x-2">
<svg class="w-7 h-7 text-green-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8c-1.657 0-3 .895-3 2s1.343 2 3 2 3 .895 3 2-1.343 2-3 2m0-8c1.11 0 2.08.402 2.599 1M12 8V7m0 1v8m0 0v1m0-1c-1.11 0-2.08-.402-2.599-1M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
<span>Cotizador Rápido</span>
</h2>
<p class="text-gray-600 text-sm mt-1">Convierte precios extranjeros a pesos argentinos</p>
</div>
<!-- Formulario -->
<div class="space-y-4">
<!-- Selección de Moneda -->
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">Moneda</label>
<select id="monedaSelect" class="w-full px-4 py-3 border-2 border-gray-300 rounded-lg focus:ring-2 focus:ring-green-500 focus:border-green-500 transition-all">
<option value="">Selecciona una moneda</option>
<?php foreach ($currencies as $code => $name): ?>
<option value="<?php echo htmlspecialchars($code); ?>"><?php echo htmlspecialchars($name); ?></option>
<?php endforeach; ?>
</select>
</div>
<!-- Toggle "En el tiempo" -->
<div class="bg-blue-50 border border-blue-200 rounded-lg p-4">
<div class="flex items-center justify-between mb-3">
<div>
<label class="block text-sm font-medium text-blue-800">En el tiempo</label>
<p class="text-xs text-blue-600 mt-1">Usar cotización histórica personalizada</p>
</div>
<label class="relative inline-flex items-center cursor-pointer">
<input type="checkbox" id="modoHistoricoToggle" class="sr-only peer">
<div class="w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-blue-300 rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-blue-600"></div>
</label>
</div>
<!-- Campo para valor histórico en USD -->
<div id="valorHistoricoContainer" class="hidden">
<label class="block text-sm font-medium text-blue-700 mb-2">
Total gastado en USD para obtener esa cantidad
</label>
<input
type="number"
id="valorHistoricoInput"
step="0.01"
placeholder="Ej: 120.00"
class="w-full px-3 py-2 border border-blue-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-all text-sm bg-blue-50"
>
<p class="text-xs text-blue-600 mt-1">Ingresa cuántos dólares gastaste para obtener esa cantidad de moneda extranjera</p>
</div>
</div>
<!-- Input de Precio -->
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">Precio en moneda extranjera</label>
<input type="number" id="precioExtranjeroInput" step="0.01" placeholder="0.00" class="w-full px-4 py-3 border-2 border-gray-300 rounded-lg focus:ring-2 focus:ring-green-500 focus:border-green-500 transition-all text-lg">
</div>
<!-- Loading -->
<div id="cotizacionLoading" class="hidden text-center py-2">
<div class="inline-block animate-spin rounded-full h-6 w-6 border-b-2 border-green-600"></div>
<p class="text-sm text-gray-600 mt-2">Obteniendo cotización...</p>
</div>
<!-- Resultado -->
<div id="resultadoCotizacion" class="hidden bg-gradient-to-r from-green-50 to-emerald-50 border-2 border-green-200 rounded-lg p-4">
<div class="text-center">
<p class="text-sm text-gray-600 mb-1">Equivalente en Pesos Argentinos</p>
<p class="text-3xl font-bold text-green-700" id="precioARS">$ 0.00</p>
<div class="mt-3 pt-3 border-t border-green-200">
<p class="text-xs text-gray-500">Cotización: <span id="tasaCotizacion" class="font-semibold">-</span></p>
<p id="tasaUsdBrl" class="text-xs text-gray-500 mt-1 hidden">Cotización: <span id="tasaUsdBrlValor" class="font-semibold">-</span></p>
</div>
</div>
</div>
<!-- Error -->
<div id="errorCotizacion" class="hidden bg-red-50 border border-red-200 text-red-700 px-4 py-3 rounded-lg text-sm">
<span class="font-medium">Error:</span> <span id="errorText">No se pudo obtener la cotización</span>
</div>
</div>
<!-- Botón Calcular -->
<button id="calcularBtn" class="w-full mt-6 bg-green-600 hover:bg-green-700 text-white font-semibold py-3 px-4 rounded-lg transition-colors">
Calcular
</button>
</div>
</div>
<!-- Login Container -->
<div class="min-h-screen flex items-center justify-center px-4">
<div class="max-w-md w-full">
<!-- Logo/Header -->
<div class="text-center mb-8">
<div class="text-6xl mb-4">💰</div>
<h1 class="text-4xl font-bold text-gray-800 mb-2">Comparador de Precios</h1>
<p class="text-gray-600">Ingresa tu PIN de 4 dígitos para continuar</p>
</div>
<!-- Login Card -->
<div id="loginCard" class="bg-white rounded-2xl shadow-2xl p-8">
<form id="loginForm" class="space-y-6">
<!-- PIN Input -->
<div>
<label for="pin" class="block text-sm font-medium text-gray-700 mb-2">
PIN de Acceso
</label>
<input
type="password"
id="pin"
inputmode="numeric"
maxlength="4"
pattern="[0-9]{4}"
class="w-full bg-white text-gray-900 px-4 py-3 text-center text-2xl tracking-widest border-2 border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 transition-all"
placeholder="••••"
required
autocomplete="off"
>
</div>
<!-- Error Message -->
<div id="errorMessage" class="hidden bg-red-50 border border-red-200 text-red-700 px-4 py-3 rounded-lg text-sm">
<span class="font-medium">Error:</span> <span id="errorText"></span>
</div>
<!-- Submit Button -->
<button
type="submit"
id="loginButton"
class="w-full bg-indigo-600 hover:bg-indigo-700 text-white font-semibold py-3 px-4 rounded-lg transition-colors duration-200 flex items-center justify-center"
>
<span id="buttonText">Ingresar</span>
<svg id="loadingSpinner" class="hidden animate-spin ml-2 h-5 w-5 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
</svg>
</button>
</form>
</div>
<!-- Country Selection (After Login) -->
<div id="countrySelection" class="hidden mt-8 space-y-4">
<h2 class="text-2xl font-bold text-gray-800 text-center mb-4">Selecciona un país</h2>
<a href="/brasil" class="block">
<div class="bg-white rounded-xl shadow-lg p-6 hover:shadow-2xl hover:-translate-y-1 transition-all duration-200">
<div class="flex items-center space-x-4">
<div class="text-5xl">🇧🇷</div>
<div class="flex-1">
<h3 class="text-xl font-bold text-gray-800">Brasil</h3>
<p class="text-gray-600 text-sm">Comparar precios con Brasil (BRL)</p>
</div>
<svg class="w-6 h-6 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"></path>
</svg>
</div>
</div>
</a>
<a href="/chile" class="block">
<div class="bg-white rounded-xl shadow-lg p-6 hover:shadow-2xl hover:-translate-y-1 transition-all duration-200">
<div class="flex items-center space-x-4">
<div class="text-5xl">🇨🇱</div>
<div class="flex-1">
<h3 class="text-xl font-bold text-gray-800">Chile</h3>
<p class="text-gray-600 text-sm">Comparar precios con Chile (CLP)</p>
</div>
<svg class="w-6 h-6 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"></path>
</svg>
</div>
</div>
</a>
</div>
</div>
</div>
<script src="/assets/js/auth.js"></script>
<script>
// ===== COTIZADOR =====
// Configuración de monedas desde PHP
const AVAILABLE_CURRENCIES = <?php echo getCurrenciesJson(); ?>;
const CURRENCY_CODES = <?php echo json_encode(getCurrencyCodes()); ?>;
const cotizarBtn = document.getElementById('cotizarBtn');
const cotizarModal = document.getElementById('cotizarModal');
const closeModalBtn = document.getElementById('closeModalBtn');
const monedaSelect = document.getElementById('monedaSelect');
const precioExtranjeroInput = document.getElementById('precioExtranjeroInput');
const calcularBtn = document.getElementById('calcularBtn');
const resultadoCotizacion = document.getElementById('resultadoCotizacion');
const cotizacionLoading = document.getElementById('cotizacionLoading');
const errorCotizacion = document.getElementById('errorCotizacion');
const precioARS = document.getElementById('precioARS');
const tasaCotizacion = document.getElementById('tasaCotizacion');
const modoHistoricoToggle = document.getElementById('modoHistoricoToggle');
const valorHistoricoContainer = document.getElementById('valorHistoricoContainer');
const valorHistoricoInput = document.getElementById('valorHistoricoInput');
let cotizaciones = {};
// Abrir modal
cotizarBtn.addEventListener('click', () => {
cotizarModal.classList.remove('hidden');
});
// Cerrar modal
closeModalBtn.addEventListener('click', () => {
cotizarModal.classList.add('hidden');
});
// Cerrar al hacer click fuera del modal
cotizarModal.addEventListener('click', (e) => {
if (e.target === cotizarModal) {
cotizarModal.classList.add('hidden');
}
});
// Toggle modo histórico
modoHistoricoToggle.addEventListener('change', () => {
if (modoHistoricoToggle.checked) {
valorHistoricoContainer.classList.remove('hidden');
// Limpiar resultados anteriores al cambiar modo
resultadoCotizacion.classList.add('hidden');
errorCotizacion.classList.add('hidden');
} else {
valorHistoricoContainer.classList.add('hidden');
valorHistoricoInput.value = '';
// Recalcular si hay datos válidos
if (monedaSelect.value && precioExtranjeroInput.value) {
calcularConversion();
}
}
});
// Obtener cotización
async function obtenerCotizacion(moneda) {
cotizacionLoading.classList.remove('hidden');
resultadoCotizacion.classList.add('hidden');
errorCotizacion.classList.add('hidden');
try {
const response = await fetch(`https://api.exchangerate-api.com/v4/latest/${moneda}`);
const data = await response.json();
if (data.rates && data.rates.ARS) {
cotizaciones[moneda] = data.rates.ARS;
return data.rates.ARS;
} else {
throw new Error('No se encontró la tasa ARS');
}
} catch (error) {
console.error('Error al obtener cotización:', error);
throw error;
} finally {
cotizacionLoading.classList.add('hidden');
}
}
// Calcular conversión
async function calcularConversion() {
const moneda = monedaSelect.value;
const precio = parseFloat(precioExtranjeroInput.value);
const modoHistorico = modoHistoricoToggle.checked;
// Validaciones
if (!moneda) {
mostrarError('Selecciona una moneda');
return;
}
if (!precio || precio <= 0) {
mostrarError('Ingresa un precio válido');
return;
}
// Validación adicional para modo histórico
if (modoHistorico) {
const totalUsdGastado = parseFloat(valorHistoricoInput.value);
if (!totalUsdGastado || totalUsdGastado <= 0) {
mostrarError('Ingresa un valor válido para el total gastado en USD');
return;
}
}
try {
let tasa, tasaUsdMoneda;
const totalUsdGastado = modoHistorico ? parseFloat(valorHistoricoInput.value) : null;
if (modoHistorico) {
// MODO HISTÓRICO: Calcular cotización histórica basada en los totales
if (moneda === 'USD') {
// Si la moneda es USD, calcular cuántos ARS costaba 1 USD en ese momento
// Cotización histórica USD-ARS = Total USD gastado / Total USD obtenido
// Como es la misma moneda, la relación es 1:1, pero necesitamos obtener la cotización ARS actual para referencia
let tasaUsdArsActual = cotizaciones['USD'];
if (!tasaUsdArsActual) {
tasaUsdArsActual = await obtenerCotizacion('USD');
}
// Para USD, usamos la relación directa del total gastado vs total obtenido
tasa = totalUsdGastado / precio; // Esto nos da cuántos USD costaba 1 USD (debería ser ~1)
// Pero para ARS, necesitamos una referencia. Asumimos que el usuario quiere saber el equivalente
// Usaremos la cotización actual como base y ajustaremos proporcionalmente
tasa = tasaUsdArsActual * (totalUsdGastado / precio);
tasaUsdMoneda = totalUsdGastado / precio;
} else {
// Para otras monedas, calcular la cotización histórica
// Cotización histórica USD → Moneda = Total moneda extranjera obtenida / Total USD gastado
const cotizacionHistoricaUsdMoneda = precio / totalUsdGastado;
// Obtener cotización actual USD-ARS para convertir a ARS
let tasaUsdArs = cotizaciones['USD'];
if (!tasaUsdArs) {
tasaUsdArs = await obtenerCotizacion('USD');
}
// Calcular tasa histórica moneda-ARS
// Si 1 USD = X moneda extranjera (histórico) y 1 USD = Y ARS (actual)
// Entonces 1 moneda extranjera = Y/X ARS
tasa = tasaUsdArs / cotizacionHistoricaUsdMoneda;
tasaUsdMoneda = cotizacionHistoricaUsdMoneda;
}
} else {
// MODO NORMAL: Usar cotizaciones actuales de la API
tasa = cotizaciones[moneda];
if (!tasa) {
tasa = await obtenerCotizacion(moneda);
}
// Obtener cotización USD para la segunda línea
if (moneda !== 'USD') {
tasaUsdMoneda = cotizaciones[`USD_${moneda}`];
if (!tasaUsdMoneda) {
const responseUsd = await fetch('https://api.exchangerate-api.com/v4/latest/USD');
const dataUsd = await responseUsd.json();
if (dataUsd.rates && dataUsd.rates[moneda]) {
tasaUsdMoneda = dataUsd.rates[moneda];
cotizaciones[`USD_${moneda}`] = tasaUsdMoneda;
}
}
} else {
tasaUsdMoneda = tasa;
}
}
// Calcular precio en ARS
const precioEnARS = precio * tasa;
// Mostrar resultado
precioARS.textContent = `$ ${precioEnARS.toLocaleString('es-AR', { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`;
// Mostrar cotización principal con indicador de modo
const modoTexto = modoHistorico ? ' (histórico)' : '';
tasaCotizacion.textContent = `1 ${moneda} = ${tasa.toFixed(2)} ARS${modoTexto}`;
// Mostrar segunda línea (USD)
const tasaUsdBrlElement = document.getElementById('tasaUsdBrl');
const tasaUsdBrlValor = document.getElementById('tasaUsdBrlValor');
if (moneda !== 'USD') {
if (tasaUsdMoneda) {
tasaUsdBrlValor.textContent = `1 USD = ${tasaUsdMoneda.toFixed(2)} ${moneda}${modoTexto}`;
tasaUsdBrlElement.classList.remove('hidden');
} else {
tasaUsdBrlElement.classList.add('hidden');
}
} else {
// Para USD, mostrar la misma tasa
tasaUsdBrlValor.textContent = `1 USD = ${tasa.toFixed(2)} ARS${modoTexto}`;
tasaUsdBrlElement.classList.remove('hidden');
}
resultadoCotizacion.classList.remove('hidden');
errorCotizacion.classList.add('hidden');
} catch (error) {
mostrarError('Error al obtener la cotización. Intenta nuevamente.');
console.error('Error en calcularConversion:', error);
}
}
// Mostrar error
function mostrarError(mensaje) {
document.getElementById('errorText').textContent = mensaje;
errorCotizacion.classList.remove('hidden');
resultadoCotizacion.classList.add('hidden');
}
// Event listeners
calcularBtn.addEventListener('click', calcularConversion);
// Calcular al presionar Enter
precioExtranjeroInput.addEventListener('keypress', (e) => {
if (e.key === 'Enter') {
calcularConversion();
}
});
// Recalcular automáticamente al cambiar moneda o precio
monedaSelect.addEventListener('change', () => {
if (precioExtranjeroInput.value) {
calcularConversion();
}
});
precioExtranjeroInput.addEventListener('input', () => {
if (monedaSelect.value && precioExtranjeroInput.value) {
// Debounce para no hacer muchas peticiones
clearTimeout(window.cotizacionTimeout);
window.cotizacionTimeout = setTimeout(calcularConversion, 500);
}
});
// Recalcular automáticamente al cambiar el valor histórico del dólar
valorHistoricoInput.addEventListener('input', () => {
if (modoHistoricoToggle.checked && monedaSelect.value && precioExtranjeroInput.value && valorHistoricoInput.value) {
// Debounce para no hacer muchas peticiones
clearTimeout(window.cotizacionHistoricoTimeout);
window.cotizacionHistoricoTimeout = setTimeout(calcularConversion, 500);
}
});
// Calcular al presionar Enter en el campo histórico
valorHistoricoInput.addEventListener('keypress', (e) => {
if (e.key === 'Enter') {
calcularConversion();
}
});
// ===== LOGIN =====
const pinInput = document.getElementById('pin');
const loginForm = document.getElementById('loginForm');
const errorMessage = document.getElementById('errorMessage');
const errorText = errorMessage.querySelector('span:last-child');
const loginButton = document.getElementById('loginButton');
const buttonText = document.getElementById('buttonText');
const loadingSpinner = document.getElementById('loadingSpinner');
const countrySelection = document.getElementById('countrySelection');
// Auto-focus en el input
pinInput.focus();
// Solo permitir números
pinInput.addEventListener('input', (e) => {
e.target.value = e.target.value.replace(/[^0-9]/g, '');
if (e.target.value.length > 4) {
e.target.value = e.target.value.slice(0, 4);
}
});
// Verificar si ya hay sesión activa
async function verificarSesionActiva() {
const sesionActiva = await authManager.verificarSesion();
if (sesionActiva) {
window.location.href = '/brasil';
}
}
// Mostrar selección de país
function mostrarSeleccionPais() {
loginForm.parentElement.classList.add('hidden');
countrySelection.classList.remove('hidden');
}
// Mostrar error
function mostrarError(mensaje) {
errorText.textContent = mensaje;
errorMessage.classList.remove('hidden');
pinInput.classList.add('border-red-500');
setTimeout(() => {
errorMessage.classList.add('hidden');
pinInput.classList.remove('border-red-500');
}, 3000);
}
// Manejar submit del formulario
loginForm.addEventListener('submit', async (e) => {
e.preventDefault();
const pin = pinInput.value;
if (pin.length !== 4) {
mostrarError('El PIN debe tener 4 dígitos');
return;
}
// Mostrar loading
loginButton.disabled = true;
buttonText.textContent = 'Verificando...';
loadingSpinner.classList.remove('hidden');
try {
const resultado = await authManager.autenticar(pin);
if (resultado.success) {
// Login exitoso
buttonText.textContent = '✓ Acceso concedido';
loginButton.classList.remove('bg-indigo-600', 'hover:bg-indigo-700');
loginButton.classList.add('bg-green-600');
setTimeout(() => {
mostrarSeleccionPais();
}, 500);
} else {
// Login fallido
mostrarError(resultado.mensaje || 'PIN incorrecto');
pinInput.value = '';
pinInput.focus();
}
} catch (error) {
mostrarError('Error de conexión');
} finally {
loginButton.disabled = false;
buttonText.textContent = 'Ingresar';
loadingSpinner.classList.add('hidden');
}
});
// Verificar sesión al cargar
verificarSesionActiva();
</script>
</body>
</html>