|
|
@@ -5,660 +5,399 @@
|
|
|
<title>Recuperar PIN - Biergarten Klein</title>
|
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
|
|
|
|
- <!-- Meta tags para evitar cache -->
|
|
|
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
|
|
|
<meta http-equiv="Pragma" content="no-cache">
|
|
|
<meta http-equiv="Expires" content="0">
|
|
|
|
|
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
|
- <link rel="stylesheet" as="style" onload="this.rel='stylesheet'"
|
|
|
- href="https://fonts.googleapis.com/css2?display=swap&family=Noto+Sans:wght@400;500;700;900&family=Spline+Sans:wght@400;500;700">
|
|
|
+ <link rel="stylesheet" href="https://fonts.googleapis.com/css2?display=swap&family=Noto+Sans:wght@400;500;700;900&family=Spline+Sans:wght@400;500;700">
|
|
|
<script src="https://cdn.tailwindcss.com?plugins=forms,container-queries"></script>
|
|
|
- <script>
|
|
|
- tailwind.config = {
|
|
|
- theme: {
|
|
|
- extend: {
|
|
|
- colors: {
|
|
|
- 'custom-dark': '#101419',
|
|
|
- 'custom-dark-hover': '#37404a',
|
|
|
- 'gray-50': '#f9fafb',
|
|
|
- 'gray-100': '#f3f4f6',
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
+ <script>
|
|
|
+ tailwind.config = {
|
|
|
+ theme: {
|
|
|
+ extend: {
|
|
|
+ colors: {
|
|
|
+ 'custom-dark': '#101419',
|
|
|
+ 'custom-dark-hover': '#37404a',
|
|
|
+ 'gray-50': '#f9fafb',
|
|
|
+ 'gray-100': '#f3f4f6',
|
|
|
+ }
|
|
|
}
|
|
|
- </script>
|
|
|
+ }
|
|
|
+ }
|
|
|
+ </script>
|
|
|
</head>
|
|
|
|
|
|
-<body class="min-h-screen bg-gray-50 flex items-center justify-center p-4" style='font-family:"Spline Sans","Noto Sans",sans-serif;'>
|
|
|
+<body class="min-h-screen bg-gray-50 flex items-center justify-center p-4 font-sans" style='font-family:"Spline Sans","Noto Sans",sans-serif;'>
|
|
|
|
|
|
<div class="w-full max-w-md">
|
|
|
- <!-- Header -->
|
|
|
<div class="text-center mb-8">
|
|
|
<div class="inline-flex items-center justify-center w-16 h-16 bg-[#101419] rounded-full mb-4">
|
|
|
<svg class="w-8 h-8 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z"/>
|
|
|
</svg>
|
|
|
</div>
|
|
|
- <h1 class="text-[26px] font-bold text-[#101419] tracking-tight mb-2">
|
|
|
- ¿Olvidaste tu PIN?
|
|
|
- </h1>
|
|
|
- <p class="text-[#58728d] text-sm leading-relaxed">
|
|
|
- No te preocupes, te ayudamos a recuperar el acceso a tu cuenta
|
|
|
- </p>
|
|
|
+ <h1 class="text-[26px] font-bold text-[#101419] tracking-tight mb-2">¿Olvidaste tu PIN?</h1>
|
|
|
+ <p class="text-[#58728d] text-sm leading-relaxed">No te preocupes, te ayudamos a recuperar el acceso a tu cuenta</p>
|
|
|
</div>
|
|
|
|
|
|
- <!-- Indicador de pasos -->
|
|
|
<div class="bg-white rounded-xl shadow-sm border border-gray-200 p-6 mb-6">
|
|
|
<div class="space-y-4">
|
|
|
<div class="flex items-start space-x-3" id="step1Indicator">
|
|
|
- <div class="flex-shrink-0 w-6 h-6 bg-[#101419] text-white rounded-full flex items-center justify-center text-xs font-medium">1</div>
|
|
|
+ <div class="step-circle flex-shrink-0 w-6 h-6 bg-[#101419] text-white rounded-full flex items-center justify-center text-xs font-medium">1</div>
|
|
|
<div class="text-sm">
|
|
|
- <p class="font-medium text-[#101419]">Ingresa tu correo</p>
|
|
|
- <p class="text-[#58728d] mt-1">Te enviaremos un código de verificación</p>
|
|
|
+ <p class="step-title font-medium text-[#101419]">Ingresa tu correo</p>
|
|
|
+ <p class="step-desc text-[#58728d] mt-1">Te enviaremos un código</p>
|
|
|
</div>
|
|
|
</div>
|
|
|
<div class="flex items-start space-x-3" id="step2Indicator">
|
|
|
- <div class="flex-shrink-0 w-6 h-6 bg-gray-300 text-gray-600 rounded-full flex items-center justify-center text-xs font-medium">2</div>
|
|
|
+ <div class="step-circle flex-shrink-0 w-6 h-6 bg-gray-300 text-gray-600 rounded-full flex items-center justify-center text-xs font-medium">2</div>
|
|
|
<div class="text-sm">
|
|
|
- <p class="font-medium text-gray-400">Código de verificación</p>
|
|
|
- <p class="text-gray-400 mt-1">Ingresa el código de 6 dígitos</p>
|
|
|
+ <p class="step-title font-medium text-gray-400">Código de verificación</p>
|
|
|
+ <p class="step-desc text-gray-400 mt-1">Ingresa el código de 6 dígitos</p>
|
|
|
</div>
|
|
|
</div>
|
|
|
<div class="flex items-start space-x-3" id="step3Indicator">
|
|
|
- <div class="flex-shrink-0 w-6 h-6 bg-gray-300 text-gray-600 rounded-full flex items-center justify-center text-xs font-medium">3</div>
|
|
|
+ <div class="step-circle flex-shrink-0 w-6 h-6 bg-gray-300 text-gray-600 rounded-full flex items-center justify-center text-xs font-medium">3</div>
|
|
|
<div class="text-sm">
|
|
|
- <p class="font-medium text-gray-400">Nuevo PIN</p>
|
|
|
- <p class="text-gray-400 mt-1">Crea tu nuevo PIN de 4 dígitos</p>
|
|
|
+ <p class="step-title font-medium text-gray-400">Nuevo PIN</p>
|
|
|
+ <p class="step-desc text-gray-400 mt-1">Crea tu nuevo PIN</p>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
- <!-- FASE 1: Ingreso de correo -->
|
|
|
<form id="emailForm" class="bg-white rounded-xl shadow-sm border border-gray-200 p-8 space-y-6">
|
|
|
<div class="text-center">
|
|
|
- <h2 class="text-[19px] font-bold text-[#101419] mb-2">Ingresa tu correo electrónico</h2>
|
|
|
- <p class="text-sm text-[#58728d]">
|
|
|
- Te enviaremos un código de verificación a tu correo
|
|
|
- </p>
|
|
|
+ <h2 class="text-[19px] font-bold text-[#101419] mb-2">Ingresa tu correo</h2>
|
|
|
+ <p class="text-sm text-[#58728d]">Te enviaremos un código de verificación</p>
|
|
|
</div>
|
|
|
-
|
|
|
- <div class="space-y-4">
|
|
|
- <div>
|
|
|
- <label for="emailInput" class="block text-sm font-medium text-[#101419] mb-2">
|
|
|
- Correo electrónico
|
|
|
- </label>
|
|
|
- <input
|
|
|
- id="emailInput"
|
|
|
- name="email"
|
|
|
- type="email"
|
|
|
- class="w-full border border-gray-300 px-4 py-3 rounded-lg focus:ring-2 focus:ring-[#101419] focus:border-transparent outline-none transition-all"
|
|
|
- placeholder="tu@email.com"
|
|
|
- required
|
|
|
- />
|
|
|
- </div>
|
|
|
+ <div>
|
|
|
+ <label for="emailInput" class="block text-sm font-medium text-[#101419] mb-2">Correo electrónico</label>
|
|
|
+ <input id="emailInput" name="email" type="email" class="w-full border border-gray-300 px-4 py-3 rounded-lg focus:ring-2 focus:ring-[#101419] outline-none" placeholder="tu@email.com" required />
|
|
|
</div>
|
|
|
-
|
|
|
<div class="space-y-3">
|
|
|
- <button
|
|
|
- id="emailSubmitBtn"
|
|
|
- type="submit"
|
|
|
- class="w-full bg-[#101419] hover:bg-[#37404a] disabled:opacity-50 disabled:cursor-not-allowed text-white py-3 rounded-lg font-medium transition-colors duration-200 focus:ring-2 focus:ring-offset-2 focus:ring-[#101419]"
|
|
|
- >
|
|
|
- Enviar código
|
|
|
- </button>
|
|
|
-
|
|
|
- <a
|
|
|
- href="/"
|
|
|
- class="block w-full text-center border border-gray-300 hover:border-[#101419] text-[#101419] py-3 rounded-lg font-medium transition-colors duration-200"
|
|
|
- >
|
|
|
- Volver al inicio
|
|
|
- </a>
|
|
|
+ <button id="emailSubmitBtn" type="submit" class="w-full bg-[#101419] hover:bg-[#37404a] text-white py-3 rounded-lg font-medium disabled:opacity-50 transition-colors">Enviar código</button>
|
|
|
+ <a href="/" class="block w-full text-center border border-gray-300 text-[#101419] py-3 rounded-lg font-medium hover:border-[#101419] transition-colors">Volver al inicio</a>
|
|
|
</div>
|
|
|
</form>
|
|
|
|
|
|
- <!-- FASE 2: Verificación de código -->
|
|
|
<form id="codeForm" class="bg-white rounded-xl shadow-sm border border-gray-200 p-8 space-y-6 hidden">
|
|
|
<div class="text-center">
|
|
|
<h2 class="text-[19px] font-bold text-[#101419] mb-2">Código de verificación</h2>
|
|
|
- <p class="text-sm text-[#58728d]">
|
|
|
- Ingresa el código de 6 dígitos que enviamos a <span id="emailDisplay" class="font-medium"></span>
|
|
|
- </p>
|
|
|
+ <p class="text-sm text-[#58728d]">Ingresa el código enviado a <span id="emailDisplay" class="font-medium"></span></p>
|
|
|
</div>
|
|
|
-
|
|
|
- <div class="space-y-4">
|
|
|
- <div>
|
|
|
- <label for="codeInput" class="block text-sm font-medium text-[#101419] mb-2">
|
|
|
- Código de verificación
|
|
|
- </label>
|
|
|
- <input
|
|
|
- id="codeInput"
|
|
|
- name="code"
|
|
|
- type="text"
|
|
|
- maxlength="6"
|
|
|
- class="w-full border border-gray-300 px-4 py-3 rounded-lg focus:ring-2 focus:ring-[#101419] focus:border-transparent outline-none transition-all text-center text-2xl tracking-widest"
|
|
|
- placeholder="000000"
|
|
|
- required
|
|
|
- />
|
|
|
- </div>
|
|
|
-
|
|
|
- <div class="text-center">
|
|
|
- <button
|
|
|
- type="button"
|
|
|
- id="resendCodeBtn"
|
|
|
- class="text-sm text-[#58728d] hover:text-[#101419] transition-colors disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:text-[#58728d]"
|
|
|
- >
|
|
|
- ¿No recibiste el código? <span class="font-medium" id="resendText">Reenviar</span>
|
|
|
- </button>
|
|
|
- </div>
|
|
|
+ <div>
|
|
|
+ <label for="codeInput" class="block text-sm font-medium text-[#101419] mb-2">Código (6 dígitos)</label>
|
|
|
+ <input id="codeInput" name="code" type="text" maxlength="6" inputmode="numeric" class="w-full border border-gray-300 px-4 py-3 rounded-lg focus:ring-2 focus:ring-[#101419] outline-none text-center text-2xl tracking-widest" placeholder="000000" required />
|
|
|
</div>
|
|
|
-
|
|
|
- <div class="space-y-3">
|
|
|
- <button
|
|
|
- id="codeSubmitBtn"
|
|
|
- type="submit"
|
|
|
- class="w-full bg-[#101419] hover:bg-[#37404a] disabled:opacity-50 disabled:cursor-not-allowed text-white py-3 rounded-lg font-medium transition-colors duration-200 focus:ring-2 focus:ring-offset-2 focus:ring-[#101419]"
|
|
|
- >
|
|
|
- Verificar código
|
|
|
- </button>
|
|
|
-
|
|
|
- <button
|
|
|
- type="button"
|
|
|
- id="backToEmailBtn"
|
|
|
- class="block w-full text-center border border-gray-300 hover:border-[#101419] text-[#101419] py-3 rounded-lg font-medium transition-colors duration-200"
|
|
|
- >
|
|
|
- Cambiar correo
|
|
|
+ <div class="text-center">
|
|
|
+ <button type="button" id="resendCodeBtn" class="text-sm text-[#58728d] hover:text-[#101419] disabled:opacity-50 transition-colors">
|
|
|
+ ¿No recibiste el código? <span class="font-medium" id="resendText">Reenviar</span>
|
|
|
</button>
|
|
|
</div>
|
|
|
+ <div class="space-y-3">
|
|
|
+ <button id="codeSubmitBtn" type="submit" class="w-full bg-[#101419] hover:bg-[#37404a] text-white py-3 rounded-lg font-medium disabled:opacity-50 transition-colors">Verificar código</button>
|
|
|
+ <button type="button" id="backToEmailBtn" class="block w-full text-center border border-gray-300 text-[#101419] py-3 rounded-lg font-medium hover:border-[#101419] transition-colors">Cambiar correo</button>
|
|
|
+ </div>
|
|
|
</form>
|
|
|
|
|
|
- <!-- FASE 3: Crear nuevo PIN -->
|
|
|
<form id="pinForm" class="bg-white rounded-xl shadow-sm border border-gray-200 p-8 space-y-6 hidden">
|
|
|
<div class="text-center">
|
|
|
<h2 class="text-[19px] font-bold text-[#101419] mb-2">Crea tu nuevo PIN</h2>
|
|
|
- <p class="text-sm text-[#58728d]">
|
|
|
- Ingresa un PIN de 4 dígitos que sea fácil de recordar para ti
|
|
|
- </p>
|
|
|
+ <p class="text-sm text-[#58728d]">Ingresa un PIN de 4 dígitos</p>
|
|
|
</div>
|
|
|
-
|
|
|
<div class="space-y-4">
|
|
|
<div>
|
|
|
- <label for="newPinInput" class="block text-sm font-medium text-[#101419] mb-2">
|
|
|
- Nuevo PIN
|
|
|
- </label>
|
|
|
- <input
|
|
|
- id="newPinInput"
|
|
|
- name="newPin"
|
|
|
- type="password"
|
|
|
- maxlength="4"
|
|
|
- class="w-full border border-gray-300 px-4 py-3 rounded-lg focus:ring-2 focus:ring-[#101419] focus:border-transparent outline-none transition-all text-center text-2xl tracking-widest"
|
|
|
- placeholder="••••"
|
|
|
- required
|
|
|
- />
|
|
|
+ <label for="newPinInput" class="block text-sm font-medium text-[#101419] mb-2">Nuevo PIN</label>
|
|
|
+ <input id="newPinInput" name="newPin" type="password" maxlength="4" inputmode="numeric" class="w-full border border-gray-300 px-4 py-3 rounded-lg focus:ring-2 focus:ring-[#101419] outline-none text-center text-2xl tracking-widest" placeholder="••••" required />
|
|
|
</div>
|
|
|
-
|
|
|
<div>
|
|
|
- <label for="confirmPinInput" class="block text-sm font-medium text-[#101419] mb-2">
|
|
|
- Confirmar PIN
|
|
|
- </label>
|
|
|
- <input
|
|
|
- id="confirmPinInput"
|
|
|
- name="confirmPin"
|
|
|
- type="password"
|
|
|
- maxlength="4"
|
|
|
- class="w-full border border-gray-300 px-4 py-3 rounded-lg focus:ring-2 focus:ring-[#101419] focus:border-transparent outline-none transition-all text-center text-2xl tracking-widest"
|
|
|
- placeholder="••••"
|
|
|
- required
|
|
|
- />
|
|
|
+ <label for="confirmPinInput" class="block text-sm font-medium text-[#101419] mb-2">Confirmar PIN</label>
|
|
|
+ <input id="confirmPinInput" name="confirmPin" type="password" maxlength="4" inputmode="numeric" class="w-full border border-gray-300 px-4 py-3 rounded-lg focus:ring-2 focus:ring-[#101419] outline-none text-center text-2xl tracking-widest" placeholder="••••" required />
|
|
|
</div>
|
|
|
-
|
|
|
- <div class="bg-blue-50 border border-blue-200 text-blue-800 px-4 py-3 rounded-lg text-sm">
|
|
|
- <div class="flex items-start space-x-2">
|
|
|
- <svg class="w-4 h-4 mt-0.5 flex-shrink-0" fill="currentColor" viewBox="0 0 20 20">
|
|
|
- <path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z" clip-rule="evenodd"/>
|
|
|
- </svg>
|
|
|
- <p>Recuerda elegir un PIN que sea seguro pero fácil de recordar. Evita fechas obvias como tu cumpleaños.</p>
|
|
|
- </div>
|
|
|
+ <div class="bg-blue-50 border border-blue-200 text-blue-800 px-4 py-3 rounded-lg text-sm flex items-start space-x-2">
|
|
|
+ <svg class="w-4 h-4 mt-0.5 flex-shrink-0" fill="currentColor" viewBox="0 0 20 20"><path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z" clip-rule="evenodd"/></svg>
|
|
|
+ <p>Elige un PIN seguro pero fácil de recordar.</p>
|
|
|
</div>
|
|
|
</div>
|
|
|
-
|
|
|
- <div class="space-y-3">
|
|
|
- <button
|
|
|
- id="pinSubmitBtn"
|
|
|
- type="submit"
|
|
|
- class="w-full bg-[#101419] hover:bg-[#37404a] disabled:opacity-50 disabled:cursor-not-allowed text-white py-3 rounded-lg font-medium transition-colors duration-200 focus:ring-2 focus:ring-offset-2 focus:ring-[#101419]"
|
|
|
- >
|
|
|
- Establecer nuevo PIN
|
|
|
- </button>
|
|
|
- </div>
|
|
|
+ <button id="pinSubmitBtn" type="submit" class="w-full bg-[#101419] hover:bg-[#37404a] text-white py-3 rounded-lg font-medium disabled:opacity-50 transition-colors">Establecer PIN</button>
|
|
|
</form>
|
|
|
|
|
|
- <!-- Mensaje de error global -->
|
|
|
- <div id="errorMessage" class="hidden bg-red-50 border border-red-200 text-red-700 px-4 py-3 rounded-lg text-sm mt-4">
|
|
|
- </div>
|
|
|
-
|
|
|
- <!-- Mensaje de éxito -->
|
|
|
- <div id="successMessage" class="hidden bg-green-50 border border-green-200 text-green-700 px-4 py-3 rounded-lg text-sm mt-4">
|
|
|
- </div>
|
|
|
+ <div id="errorMessage" class="hidden bg-red-50 border border-red-200 text-red-700 px-4 py-3 rounded-lg text-sm mt-4 text-center"></div>
|
|
|
+ <div id="successMessage" class="hidden bg-green-50 border border-green-200 text-green-700 px-4 py-3 rounded-lg text-sm mt-4 text-center"></div>
|
|
|
</div>
|
|
|
|
|
|
<script>
|
|
|
- // Variables globales
|
|
|
- let currentStep = 1;
|
|
|
- let userEmail = '';
|
|
|
- let verificationCode = '';
|
|
|
- let resendTimer = null;
|
|
|
- let resendCountdown = 0;
|
|
|
- let changeToken = '';
|
|
|
- // Referencias a elementos del DOM
|
|
|
- const emailForm = document.getElementById('emailForm');
|
|
|
- const codeForm = document.getElementById('codeForm');
|
|
|
- const pinForm = document.getElementById('pinForm');
|
|
|
-
|
|
|
- const emailInput = document.getElementById('emailInput');
|
|
|
- const codeInput = document.getElementById('codeInput');
|
|
|
- const newPinInput = document.getElementById('newPinInput');
|
|
|
- const confirmPinInput = document.getElementById('confirmPinInput');
|
|
|
-
|
|
|
- const emailSubmitBtn = document.getElementById('emailSubmitBtn');
|
|
|
- const codeSubmitBtn = document.getElementById('codeSubmitBtn');
|
|
|
- const pinSubmitBtn = document.getElementById('pinSubmitBtn');
|
|
|
-
|
|
|
- const backToEmailBtn = document.getElementById('backToEmailBtn');
|
|
|
- const resendCodeBtn = document.getElementById('resendCodeBtn');
|
|
|
- const resendText = document.getElementById('resendText');
|
|
|
-
|
|
|
- const errorMessage = document.getElementById('errorMessage');
|
|
|
- const successMessage = document.getElementById('successMessage');
|
|
|
- const emailDisplay = document.getElementById('emailDisplay');
|
|
|
-
|
|
|
- // Indicadores de pasos
|
|
|
- const step1Indicator = document.getElementById('step1Indicator');
|
|
|
- const step2Indicator = document.getElementById('step2Indicator');
|
|
|
- const step3Indicator = document.getElementById('step3Indicator');
|
|
|
-
|
|
|
- // Inicialización
|
|
|
- emailInput.focus();
|
|
|
-
|
|
|
- // Funciones de utilidad
|
|
|
- function showError(message) {
|
|
|
- errorMessage.textContent = message;
|
|
|
- errorMessage.classList.remove('hidden');
|
|
|
- successMessage.classList.add('hidden');
|
|
|
- }
|
|
|
-
|
|
|
- function showSuccess(message) {
|
|
|
- successMessage.textContent = message;
|
|
|
- successMessage.classList.remove('hidden');
|
|
|
- errorMessage.classList.add('hidden');
|
|
|
- }
|
|
|
-
|
|
|
- function hideMessages() {
|
|
|
- errorMessage.classList.add('hidden');
|
|
|
- successMessage.classList.add('hidden');
|
|
|
- }
|
|
|
-
|
|
|
- function updateStepIndicators() {
|
|
|
- // Resetear todos los indicadores
|
|
|
- [step1Indicator, step2Indicator, step3Indicator].forEach((step, index) => {
|
|
|
- const circle = step.querySelector('.w-6');
|
|
|
- const texts = step.querySelectorAll('p');
|
|
|
+ // --- ESTADO GLOBAL ---
|
|
|
+ const state = {
|
|
|
+ step: 1,
|
|
|
+ email: '',
|
|
|
+ token: '',
|
|
|
+ resendTimer: null,
|
|
|
+ resendCountdown: 0
|
|
|
+ };
|
|
|
+
|
|
|
+ // --- REFERENCIAS DOM ---
|
|
|
+ const dom = {
|
|
|
+ forms: {
|
|
|
+ email: document.getElementById('emailForm'),
|
|
|
+ code: document.getElementById('codeForm'),
|
|
|
+ pin: document.getElementById('pinForm')
|
|
|
+ },
|
|
|
+ inputs: {
|
|
|
+ email: document.getElementById('emailInput'),
|
|
|
+ code: document.getElementById('codeInput'),
|
|
|
+ newPin: document.getElementById('newPinInput'),
|
|
|
+ confirmPin: document.getElementById('confirmPinInput')
|
|
|
+ },
|
|
|
+ buttons: {
|
|
|
+ email: document.getElementById('emailSubmitBtn'),
|
|
|
+ code: document.getElementById('codeSubmitBtn'),
|
|
|
+ pin: document.getElementById('pinSubmitBtn'),
|
|
|
+ resend: document.getElementById('resendCodeBtn'),
|
|
|
+ resendText: document.getElementById('resendText'),
|
|
|
+ back: document.getElementById('backToEmailBtn')
|
|
|
+ },
|
|
|
+ messages: {
|
|
|
+ error: document.getElementById('errorMessage'),
|
|
|
+ success: document.getElementById('successMessage'),
|
|
|
+ emailDisplay: document.getElementById('emailDisplay')
|
|
|
+ },
|
|
|
+ indicators: [
|
|
|
+ document.getElementById('step1Indicator'),
|
|
|
+ document.getElementById('step2Indicator'),
|
|
|
+ document.getElementById('step3Indicator')
|
|
|
+ ]
|
|
|
+ };
|
|
|
+
|
|
|
+ // --- UTILIDADES DE UI ---
|
|
|
+ const ui = {
|
|
|
+ showError: (msg) => {
|
|
|
+ dom.messages.error.textContent = msg;
|
|
|
+ dom.messages.error.classList.remove('hidden');
|
|
|
+ dom.messages.success.classList.add('hidden');
|
|
|
+ },
|
|
|
+ showSuccess: (msg) => {
|
|
|
+ dom.messages.success.textContent = msg;
|
|
|
+ dom.messages.success.classList.remove('hidden');
|
|
|
+ dom.messages.error.classList.add('hidden');
|
|
|
+ },
|
|
|
+ hideMessages: () => {
|
|
|
+ dom.messages.error.classList.add('hidden');
|
|
|
+ dom.messages.success.classList.add('hidden');
|
|
|
+ },
|
|
|
+ setLoading: (btn, isLoading, text) => {
|
|
|
+ btn.disabled = isLoading;
|
|
|
+ btn.textContent = text;
|
|
|
+ },
|
|
|
+ updateStepIndicators: () => {
|
|
|
+ dom.indicators.forEach((el, index) => {
|
|
|
+ const circle = el.querySelector('.step-circle');
|
|
|
+ const title = el.querySelector('.step-title');
|
|
|
+ const desc = el.querySelector('.step-desc');
|
|
|
+ const stepNum = index + 1;
|
|
|
+
|
|
|
+ // Reset clases base
|
|
|
+ circle.className = 'step-circle flex-shrink-0 w-6 h-6 rounded-full flex items-center justify-center text-xs font-medium transition-colors';
|
|
|
+
|
|
|
+ if (stepNum < state.step) { // Completado
|
|
|
+ circle.classList.add('bg-green-500', 'text-white');
|
|
|
+ circle.innerHTML = '✓';
|
|
|
+ title.className = 'step-title font-medium text-green-600';
|
|
|
+ desc.className = 'step-desc mt-1 text-green-600';
|
|
|
+ } else if (stepNum === state.step) { // Actual
|
|
|
+ circle.classList.add('bg-[#101419]', 'text-white');
|
|
|
+ circle.textContent = stepNum;
|
|
|
+ title.className = 'step-title font-medium text-[#101419]';
|
|
|
+ desc.className = 'step-desc mt-1 text-[#58728d]';
|
|
|
+ } else { // Pendiente
|
|
|
+ circle.classList.add('bg-gray-300', 'text-gray-600');
|
|
|
+ circle.textContent = stepNum;
|
|
|
+ title.className = 'step-title font-medium text-gray-400';
|
|
|
+ desc.className = 'step-desc mt-1 text-gray-400';
|
|
|
+ }
|
|
|
+ });
|
|
|
+ },
|
|
|
+ setStep: (step) => {
|
|
|
+ state.step = step;
|
|
|
+ // Ocultar todos
|
|
|
+ Object.values(dom.forms).forEach(f => f.classList.add('hidden'));
|
|
|
|
|
|
- if (index + 1 < currentStep) {
|
|
|
- // Paso completado
|
|
|
- circle.className = 'flex-shrink-0 w-6 h-6 bg-green-500 text-white rounded-full flex items-center justify-center text-xs font-medium';
|
|
|
- circle.innerHTML = '✓';
|
|
|
- texts.forEach(text => {
|
|
|
- text.className = text.className.replace('text-gray-400', 'text-green-600');
|
|
|
- text.className = text.className.replace('text-[#58728d]', 'text-green-600');
|
|
|
- });
|
|
|
- } else if (index + 1 === currentStep) {
|
|
|
- // Paso actual
|
|
|
- circle.className = 'flex-shrink-0 w-6 h-6 bg-[#101419] text-white rounded-full flex items-center justify-center text-xs font-medium';
|
|
|
- circle.textContent = index + 1;
|
|
|
- texts.forEach(text => {
|
|
|
- text.className = text.className.replace('text-gray-400', 'text-[#101419]');
|
|
|
- if (text.className.includes('font-medium')) {
|
|
|
- text.className = text.className.replace('text-gray-400', 'text-[#101419]');
|
|
|
- } else {
|
|
|
- text.className = text.className.replace('text-gray-400', 'text-[#58728d]');
|
|
|
- }
|
|
|
- });
|
|
|
- } else {
|
|
|
- // Paso pendiente
|
|
|
- circle.className = 'flex-shrink-0 w-6 h-6 bg-gray-300 text-gray-600 rounded-full flex items-center justify-center text-xs font-medium';
|
|
|
- circle.textContent = index + 1;
|
|
|
- texts.forEach(text => {
|
|
|
- text.className = text.className.replace('text-[#101419]', 'text-gray-400');
|
|
|
- text.className = text.className.replace('text-[#58728d]', 'text-gray-400');
|
|
|
- text.className = text.className.replace('text-green-600', 'text-gray-400');
|
|
|
- });
|
|
|
- }
|
|
|
- });
|
|
|
- }
|
|
|
+ // Gestionar temporizador
|
|
|
+ if (step !== 2) actions.stopResendTimer();
|
|
|
+ if (step === 2) actions.startResendTimer();
|
|
|
|
|
|
- function showStep(step) {
|
|
|
- // Ocultar todos los formularios
|
|
|
- emailForm.classList.add('hidden');
|
|
|
- codeForm.classList.add('hidden');
|
|
|
- pinForm.classList.add('hidden');
|
|
|
-
|
|
|
- // Detener timer si cambia de paso
|
|
|
- if (step !== 2) {
|
|
|
- stopResendTimer();
|
|
|
- }
|
|
|
-
|
|
|
- // Mostrar el formulario correspondiente
|
|
|
- switch(step) {
|
|
|
- case 1:
|
|
|
- emailForm.classList.remove('hidden');
|
|
|
- emailInput.focus();
|
|
|
- break;
|
|
|
- case 2:
|
|
|
- codeForm.classList.remove('hidden');
|
|
|
- codeInput.focus();
|
|
|
- break;
|
|
|
- case 3:
|
|
|
- pinForm.classList.remove('hidden');
|
|
|
- newPinInput.focus();
|
|
|
- break;
|
|
|
- }
|
|
|
-
|
|
|
- currentStep = step;
|
|
|
- updateStepIndicators();
|
|
|
- hideMessages();
|
|
|
- }
|
|
|
+ // Mostrar actual
|
|
|
+ if (step === 1) { dom.forms.email.classList.remove('hidden'); dom.inputs.email.focus(); }
|
|
|
+ if (step === 2) { dom.forms.code.classList.remove('hidden'); dom.inputs.code.focus(); }
|
|
|
+ if (step === 3) { dom.forms.pin.classList.remove('hidden'); dom.inputs.newPin.focus(); }
|
|
|
|
|
|
- function generateVerificationCode() {
|
|
|
- return Math.floor(100000 + Math.random() * 900000).toString();
|
|
|
- }
|
|
|
-
|
|
|
- function startResendTimer() {
|
|
|
- resendCountdown = 60; // 1 minuto
|
|
|
- resendCodeBtn.disabled = true;
|
|
|
-
|
|
|
- resendTimer = setInterval(() => {
|
|
|
- if (resendCountdown > 0) {
|
|
|
- resendText.textContent = `Reenviar (${resendCountdown}s)`;
|
|
|
- resendCountdown--;
|
|
|
- } else {
|
|
|
- clearInterval(resendTimer);
|
|
|
- resendCodeBtn.disabled = false;
|
|
|
- resendText.textContent = 'Reenviar';
|
|
|
- }
|
|
|
- }, 1000);
|
|
|
- }
|
|
|
-
|
|
|
- async function sendMail(){
|
|
|
+ ui.updateStepIndicators();
|
|
|
+ ui.hideMessages();
|
|
|
+ }
|
|
|
+ };
|
|
|
|
|
|
- const codeVerify = await fetch('/recovery/validate', {
|
|
|
+ // --- LOGICA DE NEGOCIO (API) ---
|
|
|
+ const api = {
|
|
|
+ sendCode: async (email) => {
|
|
|
+ const res = await fetch('/recovery', {
|
|
|
method: 'POST',
|
|
|
- headers: {
|
|
|
- 'Content-Type': 'application/json'
|
|
|
- },
|
|
|
- body: JSON.stringify({ email: userEmail, code })
|
|
|
+ headers: { 'Content-Type': 'application/json' },
|
|
|
+ body: JSON.stringify({ email })
|
|
|
});
|
|
|
-
|
|
|
- switch (codeVerify.status) {
|
|
|
- case 200:
|
|
|
- showSuccess('Código verificado correctamente');
|
|
|
- setTimeout(async () => {
|
|
|
- const responseJSON = await codeVerify.json();
|
|
|
- changeToken = responseJSON.data.token;
|
|
|
- showStep(3);
|
|
|
- }, 1000);
|
|
|
- break;
|
|
|
- case 404:
|
|
|
- showError('Usuario no encontrado. Por favor verifica e intenta nuevamente.');
|
|
|
- codeInput.focus();
|
|
|
- codeInput.select();
|
|
|
- return;
|
|
|
- case 400:
|
|
|
- showError('Código incorrecto. Por favor verifica e intenta nuevamente.');
|
|
|
- codeInput.focus();
|
|
|
- codeInput.select();
|
|
|
- return;
|
|
|
- default:
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- codeSubmitBtn.disabled = false;
|
|
|
- codeSubmitBtn.textContent = 'Verificar código';
|
|
|
- }
|
|
|
-
|
|
|
- function stopResendTimer() {
|
|
|
- if (resendTimer) {
|
|
|
- clearInterval(resendTimer);
|
|
|
- resendTimer = null;
|
|
|
- }
|
|
|
- resendCountdown = 0;
|
|
|
- resendCodeBtn.disabled = false;
|
|
|
- resendText.textContent = 'Reenviar';
|
|
|
- }
|
|
|
-
|
|
|
- // FASE 1: Envío de correo
|
|
|
- emailForm.addEventListener('submit', async function(e) {
|
|
|
- e.preventDefault();
|
|
|
-
|
|
|
- const email = emailInput.value.trim();
|
|
|
-
|
|
|
- if (!email) {
|
|
|
- showError('Por favor ingresa tu correo electrónico');
|
|
|
- emailInput.focus();
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- if (!isValidEmail(email)) {
|
|
|
- showError('Por favor ingresa un correo electrónico válido');
|
|
|
- emailInput.focus();
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- emailSubmitBtn.disabled = true;
|
|
|
- emailSubmitBtn.textContent = 'Enviando código...';
|
|
|
-
|
|
|
- userEmail = email;
|
|
|
- emailDisplay.textContent = email;
|
|
|
- const body = JSON.stringify({ email })
|
|
|
- // En una aplicación real, aquí enviarías el código por email
|
|
|
- const emailResponse = await fetch('/recovery', {
|
|
|
+ if (!res.ok) throw new Error((await res.json()).message || 'Error al enviar código');
|
|
|
+ return res.json();
|
|
|
+ },
|
|
|
+ validateCode: async (email, code) => {
|
|
|
+ const res = await fetch('/recovery/validate', {
|
|
|
method: 'POST',
|
|
|
- headers: {
|
|
|
- 'Content-Type': 'application/json'
|
|
|
- },
|
|
|
- body: body
|
|
|
+ headers: { 'Content-Type': 'application/json' },
|
|
|
+ body: JSON.stringify({ email, code })
|
|
|
});
|
|
|
-
|
|
|
- if (!emailResponse.ok) {
|
|
|
- showError(emailResponse.message || 'Error al enviar el código. Por favor intenta nuevamente.');
|
|
|
- emailInput.focus();
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- showSuccess(`Código enviado a ${email}`);
|
|
|
-
|
|
|
- setTimeout(() => {
|
|
|
- showStep(2);
|
|
|
- startResendTimer(); // Iniciar el timer cuando se muestra la fase 2
|
|
|
- }, 400);
|
|
|
-
|
|
|
- emailSubmitBtn.disabled = false;
|
|
|
- emailSubmitBtn.textContent = 'Enviar código';
|
|
|
- });
|
|
|
-
|
|
|
- // FASE 2: Verificación de código
|
|
|
- codeForm.addEventListener('submit',async function(e) {
|
|
|
- e.preventDefault();
|
|
|
-
|
|
|
- const code = codeInput.value.trim();
|
|
|
-
|
|
|
- if (!code || code.length !== 6) {
|
|
|
- showError('Por favor ingresa el código de 6 dígitos');
|
|
|
- codeInput.focus();
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- if (!/^\d{6}$/.test(code)) {
|
|
|
- showError('El código debe contener solo números');
|
|
|
- codeInput.focus();
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- codeSubmitBtn.disabled = true;
|
|
|
- codeSubmitBtn.textContent = 'Verificando...';
|
|
|
-
|
|
|
-
|
|
|
- const codeVerify = await fetch('/recovery/validate', {
|
|
|
- method: 'POST',
|
|
|
- headers: {
|
|
|
- 'Content-Type': 'application/json'
|
|
|
- },
|
|
|
- body: JSON.stringify({ email: userEmail, code })
|
|
|
- });
|
|
|
-
|
|
|
- switch (codeVerify.status) {
|
|
|
- case 200:
|
|
|
- showSuccess('Código verificado correctamente');
|
|
|
- setTimeout(async () => {
|
|
|
- data = await codeVerify.json();
|
|
|
- changeToken = data.token;
|
|
|
- showStep(3);
|
|
|
- }, 1000);
|
|
|
- break;
|
|
|
- case 404:
|
|
|
- showError('Usuario no encontrado. Por favor verifica e intenta nuevamente.');
|
|
|
- codeInput.focus();
|
|
|
- codeInput.select();
|
|
|
- return;
|
|
|
- case 400:
|
|
|
- showError('Código incorrecto. Por favor verifica e intenta nuevamente.');
|
|
|
- codeSubmitBtn.disabled = false;
|
|
|
- codeSubmitBtn.textContent = 'Verificar código';
|
|
|
- codeInput.focus();
|
|
|
- codeInput.select();
|
|
|
- return;
|
|
|
- default:
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- codeSubmitBtn.disabled = false;
|
|
|
- codeSubmitBtn.textContent = 'Verificar código';
|
|
|
-
|
|
|
- });
|
|
|
-
|
|
|
- // FASE 3: Crear nuevo PIN
|
|
|
- pinForm.addEventListener('submit', async function(e) {
|
|
|
- e.preventDefault();
|
|
|
-
|
|
|
- const newPin = newPinInput.value.trim();
|
|
|
- const confirmPin = confirmPinInput.value.trim();
|
|
|
-
|
|
|
- if (!newPin || newPin.length !== 4) {
|
|
|
- showError('El PIN debe tener 4 dígitos');
|
|
|
- newPinInput.focus();
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- if (!/^\d{4}$/.test(newPin)) {
|
|
|
- showError('El PIN debe contener solo números');
|
|
|
- newPinInput.focus();
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- if (!confirmPin || confirmPin.length !== 4) {
|
|
|
- showError('Por favor confirma tu PIN');
|
|
|
- confirmPinInput.focus();
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- if (newPin !== confirmPin) {
|
|
|
- showError('Los PINs no coinciden. Por favor verifica e intenta nuevamente.');
|
|
|
- confirmPinInput.focus();
|
|
|
- confirmPinInput.select();
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- pinSubmitBtn.disabled = true;
|
|
|
- pinSubmitBtn.textContent = 'Estableciendo PIN...';
|
|
|
-
|
|
|
-
|
|
|
- const changePinResponse = await fetch('/api/users/pin-recovery', {
|
|
|
+ const data = await res.json();
|
|
|
+ if (res.status === 404) throw new Error('Usuario no encontrado');
|
|
|
+ if (res.status === 400) throw new Error('Código incorrecto');
|
|
|
+ if (!res.ok) throw new Error(data.message || 'Error de validación');
|
|
|
+ return data; // Debe contener { data: { token: '...' } }
|
|
|
+ },
|
|
|
+ setPin: async (token, new_pin) => {
|
|
|
+ const res = await fetch('/api/users/pin-recovery', {
|
|
|
method: 'POST',
|
|
|
- headers: {
|
|
|
+ headers: {
|
|
|
'Content-Type': 'application/json',
|
|
|
- 'Authorization': 'Bearer ' + changeToken
|
|
|
+ 'Authorization': 'Bearer ' + token
|
|
|
},
|
|
|
- body: JSON.stringify({email:userEmail, token: changeToken, new_pin: newPin})
|
|
|
+ body: JSON.stringify({ email: state.email, token, new_pin })
|
|
|
});
|
|
|
- if (changePinResponse.ok) {
|
|
|
- showSuccess('¡PIN establecido correctamente! Ya puedes acceder a tu cuenta.');
|
|
|
- setTimeout(() => {
|
|
|
- // Redirigir al login o página principal
|
|
|
- alert('PIN establecido correctamente. Serás redirigido al inicio de sesión.');
|
|
|
- window.location.href = '/';
|
|
|
- }, 500);
|
|
|
- } else {
|
|
|
- showError('Error al establecer el PIN. Por favor intenta nuevamente.');
|
|
|
+ if (!res.ok) throw new Error('Error al establecer el PIN');
|
|
|
+ return res.json();
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ // --- ACCIONES ---
|
|
|
+ const actions = {
|
|
|
+ requestEmailCode: async () => {
|
|
|
+ const email = dom.inputs.email.value.trim();
|
|
|
+ if (!email || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
|
|
|
+ return ui.showError('Ingresa un correo válido');
|
|
|
}
|
|
|
|
|
|
+ ui.setLoading(dom.buttons.email, true, 'Enviando...');
|
|
|
|
|
|
-
|
|
|
- });
|
|
|
+ try {
|
|
|
+ await api.sendCode(email);
|
|
|
+ state.email = email;
|
|
|
+ dom.messages.emailDisplay.textContent = email;
|
|
|
+ ui.showSuccess(`Código enviado a ${email}`);
|
|
|
+ setTimeout(() => ui.setStep(2), 500);
|
|
|
+ } catch (err) {
|
|
|
+ ui.showError(err.message);
|
|
|
+ } finally {
|
|
|
+ ui.setLoading(dom.buttons.email, false, 'Enviar código');
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ submitVerificationCode: async () => {
|
|
|
+ const code = dom.inputs.code.value.trim();
|
|
|
+ if (!code || code.length !== 6) return ui.showError('Ingresa el código de 6 dígitos');
|
|
|
+
|
|
|
+ ui.setLoading(dom.buttons.code, true, 'Verificando...');
|
|
|
+
|
|
|
+ try {
|
|
|
+ const response = await api.validateCode(state.email, code);
|
|
|
+ // Ajuste robusto: a veces la API devuelve data.token o data.data.token
|
|
|
+ state.token = response.token || (response.data && response.data.token);
|
|
|
+
|
|
|
+ if (!state.token) throw new Error('Token de seguridad no recibido');
|
|
|
+
|
|
|
+ ui.showSuccess('Código verificado');
|
|
|
+ setTimeout(() => ui.setStep(3), 500);
|
|
|
+ } catch (err) {
|
|
|
+ ui.showError(err.message);
|
|
|
+ dom.inputs.code.select();
|
|
|
+ } finally {
|
|
|
+ ui.setLoading(dom.buttons.code, false, 'Verificar código');
|
|
|
+ }
|
|
|
+ },
|
|
|
|
|
|
- // Eventos adicionales
|
|
|
- backToEmailBtn.addEventListener('click', function() {
|
|
|
- stopResendTimer(); // Detener el timer si regresa al paso 1
|
|
|
- showStep(1);
|
|
|
- });
|
|
|
+ submitNewPin: async () => {
|
|
|
+ const pin = dom.inputs.newPin.value;
|
|
|
+ const confirm = dom.inputs.confirmPin.value;
|
|
|
|
|
|
- resendCodeBtn.addEventListener('click', function() {
|
|
|
- if (resendCodeBtn.disabled) return; // Prevenir clics múltiples
|
|
|
+ if (pin.length !== 4) return ui.showError('El PIN debe tener 4 dígitos');
|
|
|
+ if (pin !== confirm) return ui.showError('Los PINs no coinciden');
|
|
|
|
|
|
- sendMail();
|
|
|
+ ui.setLoading(dom.buttons.pin, true, 'Guardando...');
|
|
|
|
|
|
- startResendTimer(); // Reiniciar el timer
|
|
|
- });
|
|
|
+ try {
|
|
|
+ await api.setPin(state.token, pin);
|
|
|
+ ui.showSuccess('¡PIN actualizado! Redirigiendo...');
|
|
|
+ setTimeout(() => window.location.href = '/', 1500);
|
|
|
+ } catch (err) {
|
|
|
+ ui.showError(err.message);
|
|
|
+ ui.setLoading(dom.buttons.pin, false, 'Establecer PIN');
|
|
|
+ }
|
|
|
+ },
|
|
|
|
|
|
- // Formatear inputs de PIN para que solo acepten números
|
|
|
- [newPinInput, confirmPinInput].forEach(input => {
|
|
|
- input.addEventListener('input', function(e) {
|
|
|
- e.target.value = e.target.value.replace(/\D/g, '').slice(0, 4);
|
|
|
- hideMessages();
|
|
|
- });
|
|
|
- });
|
|
|
+ startResendTimer: () => {
|
|
|
+ state.resendCountdown = 60;
|
|
|
+ dom.buttons.resend.disabled = true;
|
|
|
+ actions.updateTimerText();
|
|
|
+
|
|
|
+ state.resendTimer = setInterval(() => {
|
|
|
+ state.resendCountdown--;
|
|
|
+ actions.updateTimerText();
|
|
|
+ if (state.resendCountdown <= 0) actions.stopResendTimer();
|
|
|
+ }, 1000);
|
|
|
+ },
|
|
|
+
|
|
|
+ stopResendTimer: () => {
|
|
|
+ if (state.resendTimer) clearInterval(state.resendTimer);
|
|
|
+ state.resendTimer = null;
|
|
|
+ state.resendCountdown = 0;
|
|
|
+ dom.buttons.resend.disabled = false;
|
|
|
+ dom.buttons.resendText.textContent = 'Reenviar';
|
|
|
+ },
|
|
|
+
|
|
|
+ updateTimerText: () => {
|
|
|
+ if (state.resendCountdown > 0) {
|
|
|
+ dom.buttons.resendText.textContent = `Reenviar (${state.resendCountdown}s)`;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ };
|
|
|
|
|
|
- // Formatear input de código para que solo acepte números
|
|
|
- codeInput.addEventListener('input', function(e) {
|
|
|
- e.target.value = e.target.value.replace(/\D/g, '').slice(0, 6);
|
|
|
- hideMessages();
|
|
|
+ // --- EVENT LISTENERS ---
|
|
|
+
|
|
|
+ // Forms Submits
|
|
|
+ dom.forms.email.addEventListener('submit', (e) => { e.preventDefault(); actions.requestEmailCode(); });
|
|
|
+ dom.forms.code.addEventListener('submit', (e) => { e.preventDefault(); actions.submitVerificationCode(); });
|
|
|
+ dom.forms.pin.addEventListener('submit', (e) => { e.preventDefault(); actions.submitNewPin(); });
|
|
|
+
|
|
|
+ // Buttons
|
|
|
+ dom.buttons.back.addEventListener('click', () => ui.setStep(1));
|
|
|
+ dom.buttons.resend.addEventListener('click', () => {
|
|
|
+ if (!dom.buttons.resend.disabled) {
|
|
|
+ actions.stopResendTimer(); // Resetear timer actual si existe
|
|
|
+ actions.requestEmailCode(); // Reutilizar lógica de envío
|
|
|
+ // requestEmailCode maneja el timer al pasar al step 2,
|
|
|
+ // pero como ya estamos en step 2, forzamos el inicio del timer:
|
|
|
+ actions.startResendTimer();
|
|
|
+ }
|
|
|
});
|
|
|
|
|
|
- // Ocultar mensajes cuando el usuario escriba
|
|
|
- [emailInput, codeInput, newPinInput, confirmPinInput].forEach(input => {
|
|
|
- input.addEventListener('input', hideMessages);
|
|
|
+ // Inputs Formatting
|
|
|
+ [dom.inputs.code, dom.inputs.newPin, dom.inputs.confirmPin].forEach(input => {
|
|
|
+ input.addEventListener('input', (e) => {
|
|
|
+ e.target.value = e.target.value.replace(/\D/g, ''); // Solo números
|
|
|
+ ui.hideMessages();
|
|
|
+ });
|
|
|
});
|
|
|
+ dom.inputs.email.addEventListener('input', ui.hideMessages);
|
|
|
|
|
|
- // Validar email
|
|
|
- function isValidEmail(email) {
|
|
|
- const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
|
- return emailRegex.test(email);
|
|
|
- }
|
|
|
-
|
|
|
- // Navegación con Enter entre campos de PIN
|
|
|
- newPinInput.addEventListener('keypress', function(e) {
|
|
|
- if (e.key === 'Enter' && this.value.length === 4) {
|
|
|
- confirmPinInput.focus();
|
|
|
- }
|
|
|
- });
|
|
|
+ // Inicialización
|
|
|
+ dom.inputs.email.focus();
|
|
|
|
|
|
- confirmPinInput.addEventListener('keypress', function(e) {
|
|
|
- if (e.key === 'Enter' && this.value.length === 4) {
|
|
|
- pinForm.dispatchEvent(new Event('submit'));
|
|
|
- }
|
|
|
- });
|
|
|
</script>
|
|
|
-
|
|
|
</body>
|
|
|
</html>
|