782 lines
24 KiB
PHP
782 lines
24 KiB
PHP
<?php
|
|
include ("header.php");
|
|
include (__DIR__."/php/funciones.php");
|
|
|
|
$cursos = obtenerCursos();
|
|
$cupones = obtenerCupones();
|
|
function obtenerNombreCursoPorId($cursos, $idBuscado) {
|
|
foreach ($cursos as $curso) {
|
|
if (isset($curso['id']) && $curso['id'] === $idBuscado) {
|
|
return $curso['name'] ?? null;
|
|
}
|
|
}
|
|
return null; // Si no se encuentra
|
|
}
|
|
?>
|
|
<style>
|
|
/* Main Content */
|
|
.api-container {
|
|
max-width: 600px;
|
|
margin: 0 auto;
|
|
}
|
|
|
|
.black-box {
|
|
background-color: #000000;
|
|
color: #00ff00;
|
|
padding: 20px;
|
|
border-radius: 8px;
|
|
border: 2px solid #333;
|
|
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3);
|
|
font-family: 'Courier New', monospace;
|
|
font-size: 14px;
|
|
line-height: 1.6;
|
|
}
|
|
|
|
.api-label {
|
|
color: #ffffff;
|
|
font-weight: bold;
|
|
margin-bottom: 10px;
|
|
font-size: 16px;
|
|
}
|
|
|
|
.api-key {
|
|
background-color: #1a1a1a;
|
|
padding: 10px;
|
|
border: 1px solid #444;
|
|
border-radius: 4px;
|
|
color: #00ff00;
|
|
font-family: 'Courier New', monospace;
|
|
word-break: break-all;
|
|
position: relative;
|
|
margin-bottom: 15px;
|
|
}
|
|
|
|
.copy-button {
|
|
background-color: #333;
|
|
color: #00ff00;
|
|
border: 1px solid #555;
|
|
padding: 8px 16px;
|
|
border-radius: 4px;
|
|
cursor: pointer;
|
|
font-family: 'Courier New', monospace;
|
|
font-size: 12px;
|
|
transition: background-color 0.3s;
|
|
}
|
|
|
|
.copy-button:hover {
|
|
background-color: #444;
|
|
}
|
|
|
|
.copy-button:active {
|
|
background-color: #555;
|
|
}
|
|
|
|
.status-text {
|
|
color: #ffff00;
|
|
font-size: 12px;
|
|
margin-top: 10px;
|
|
}
|
|
|
|
.main-content {
|
|
flex: 1;
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
padding: 2rem;
|
|
}
|
|
.form-container {
|
|
background: rgba(255, 255, 255, 0.95);
|
|
backdrop-filter: blur(15px);
|
|
border-radius: 20px;
|
|
padding: 3rem;
|
|
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
|
|
border: 1px solid rgba(255, 255, 255, 0.2);
|
|
width: 100%;
|
|
max-width: 600px;
|
|
animation: slideUp 0.6s ease-out;
|
|
}
|
|
|
|
@keyframes slideUp {
|
|
from {
|
|
opacity: 0;
|
|
transform: translateY(30px);
|
|
}
|
|
to {
|
|
opacity: 1;
|
|
transform: translateY(0);
|
|
}
|
|
}
|
|
|
|
.form-title {
|
|
text-align: center;
|
|
color: #2d3748;
|
|
font-size: 2rem;
|
|
font-weight: bold;
|
|
margin-bottom: 0.5rem;
|
|
}
|
|
|
|
.form-subtitle {
|
|
text-align: center;
|
|
color: #718096;
|
|
margin-bottom: 2rem;
|
|
}
|
|
|
|
.form-group {
|
|
margin-bottom: 1.5rem;
|
|
}
|
|
|
|
.form-label {
|
|
display: block;
|
|
margin-bottom: 0.5rem;
|
|
color: #4a5568;
|
|
font-weight: 600;
|
|
font-size: 0.9rem;
|
|
}
|
|
|
|
.form-input {
|
|
width: 100%;
|
|
padding: 0.875rem 1rem;
|
|
border: 2px solid #e2e8f0;
|
|
border-radius: 12px;
|
|
font-size: 1rem;
|
|
transition: all 0.3s ease;
|
|
background: rgba(255, 255, 255, 0.8);
|
|
}
|
|
|
|
.form-input:focus {
|
|
outline: none;
|
|
border-color: #4c51bf;
|
|
box-shadow: 0 0 0 3px rgba(76, 81, 191, 0.1);
|
|
background: rgba(255, 255, 255, 1);
|
|
}
|
|
|
|
.form-textarea {
|
|
min-height: 100px;
|
|
resize: vertical;
|
|
font-family: 'Courier New', monospace;
|
|
font-size: 0.875rem;
|
|
}
|
|
|
|
.file-input-wrapper {
|
|
position: relative;
|
|
display: inline-block;
|
|
width: 100%;
|
|
}
|
|
|
|
.file-input {
|
|
position: absolute;
|
|
left: -9999px;
|
|
}
|
|
|
|
.file-input-label {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
gap: 0.5rem;
|
|
padding: 0.875rem 1rem;
|
|
border: 2px dashed #cbd5e0;
|
|
border-radius: 12px;
|
|
cursor: pointer;
|
|
transition: all 0.3s ease;
|
|
background: rgba(247, 250, 252, 0.8);
|
|
color: #718096;
|
|
}
|
|
|
|
.file-input-label:hover {
|
|
border-color: #4c51bf;
|
|
background: rgba(76, 81, 191, 0.05);
|
|
color: #4c51bf;
|
|
}
|
|
|
|
.file-input-label.has-file {
|
|
border-color: #48bb78;
|
|
background: rgba(72, 187, 120, 0.1);
|
|
color: #48bb78;
|
|
}
|
|
|
|
.submit-btn {
|
|
width: 100%;
|
|
padding: 1rem;
|
|
background: linear-gradient(135deg, #4c51bf 0%, #667eea 100%);
|
|
color: white;
|
|
border: none;
|
|
border-radius: 12px;
|
|
font-size: 1.1rem;
|
|
font-weight: 600;
|
|
cursor: pointer;
|
|
transition: all 0.3s ease;
|
|
margin-top: 1rem;
|
|
}
|
|
|
|
.submit-btn:hover {
|
|
transform: translateY(-2px);
|
|
box-shadow: 0 10px 25px rgba(76, 81, 191, 0.3);
|
|
}
|
|
|
|
.submit-btn:active {
|
|
transform: translateY(0);
|
|
}
|
|
select.form-input {
|
|
cursor: pointer;
|
|
background-position: right 0.75rem center;
|
|
background-repeat: no-repeat;
|
|
background-size: 1.5em 1.5em;
|
|
padding-right: 2.5rem;
|
|
appearance: none;
|
|
-webkit-appearance: none;
|
|
-moz-appearance: none;
|
|
}
|
|
|
|
|
|
</style>
|
|
<main class="app-main">
|
|
<!--begin::App Content Header-->
|
|
<div class="main-content">
|
|
<div class="form-container">
|
|
<h1 class="form-title">Registrar Usuario</h1>
|
|
<p class="form-subtitle">Complete los datos para crear un nuevo usuario en el sistema</p>
|
|
|
|
<form id="userForm">
|
|
<div class="form-group">
|
|
<label for="empresa" class="form-label">Nombre de la Empresa *</label>
|
|
<input type="text" id="empresa" name="empresa" class="form-input" required
|
|
placeholder="Ej: Mi Empresa S.A.">
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label for="usuario" class="form-label">Nombre de Usuario *</label>
|
|
<input type="text" id="usuario" name="usuario" class="form-input" required
|
|
placeholder="Ej: admin_empresa">
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label for="email" class="form-label">Correo Electrónico *</label>
|
|
<input type="email" id="email" name="email" class="form-input" required
|
|
placeholder="usuario@empresa.com">
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label for="platf_pago" class="form-label">Plataforma de pago*</label>
|
|
<select id="platf_pago" name="platf_pago" class="form-input" required>
|
|
<option value="">Seleccionar una plataforma (default: stripe)</option>
|
|
<option value="stripe">Stripe</option>
|
|
<option value="openpay">OpenPay</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label for="clavePublica" class="form-label">Clave Pública *</label>
|
|
<textarea id="clavePublica" name="clavePublica" class="form-input form-textarea" required
|
|
placeholder="-----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA... -----END PUBLIC KEY-----"></textarea>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label for="clavePrivada" class="form-label">Clave Privada *</label>
|
|
<textarea id="clavePrivada" name="clavePrivada" class="form-input form-textarea" required
|
|
placeholder="-----BEGIN PRIVATE KEY----- MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC... -----END PRIVATE KEY-----"></textarea>
|
|
</div>
|
|
|
|
<button type="submit" class="submit-btn">
|
|
✨ Registrar Usuario
|
|
</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
<!--end::App Main-->
|
|
<div id="toast" class="toast"></div>
|
|
</main>
|
|
<script>
|
|
// ============================================================================
|
|
// JAVASCRIPT PARA ENVÍO DE FORMULARIO A ARCHIVO PHP
|
|
// ============================================================================
|
|
|
|
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
const form = document.getElementById('userForm');
|
|
const submitBtn = form.querySelector('.submit-btn');
|
|
|
|
// Configuración del archivo PHP intermedio
|
|
const PHP_CONFIG = {
|
|
endpoint: 'php/process_user.php', // Archivo PHP que procesará los datos
|
|
timeout: 30000 // 30 segundos
|
|
};
|
|
|
|
// Manejar envío del formulario
|
|
form.addEventListener('submit', async function(e) {
|
|
e.preventDefault();
|
|
|
|
// Deshabilitar botón y cambiar texto
|
|
const originalText = submitBtn.textContent;
|
|
submitBtn.disabled = true;
|
|
submitBtn.textContent = '⏳ Procesando...';
|
|
|
|
try {
|
|
// Crear FormData para manejar archivos si los hay
|
|
const formData = new FormData(form);
|
|
|
|
// Validar datos antes de enviar
|
|
if (!validateForm(formData)) {
|
|
return;
|
|
}
|
|
|
|
// Establecer valor por defecto para plataforma si está vacío
|
|
if (!formData.get('platf_pago')) {
|
|
formData.set('platf_pago', 'stripe');
|
|
}
|
|
|
|
// Enviar datos al archivo PHP
|
|
const response = await sendToPHP(formData);
|
|
|
|
// Procesar respuesta
|
|
if (response.success) {
|
|
handleSuccess(response);
|
|
} else {
|
|
handleError(response);
|
|
}
|
|
|
|
} catch (error) {
|
|
handleNetworkError(error);
|
|
} finally {
|
|
// Restaurar botón
|
|
submitBtn.disabled = false;
|
|
submitBtn.textContent = originalText;
|
|
}
|
|
});
|
|
|
|
/**
|
|
* Enviar datos al archivo PHP intermedio
|
|
*/
|
|
async function sendToPHP(formData) {
|
|
const url = PHP_CONFIG.endpoint;
|
|
|
|
const requestOptions = {
|
|
method: 'POST',
|
|
body: formData
|
|
};
|
|
|
|
console.log('Enviando datos a archivo PHP:', url);
|
|
console.log('Datos del formulario:', Object.fromEntries(formData));
|
|
|
|
const response = await fetch(url, requestOptions);
|
|
|
|
// Verificar si la respuesta es JSON válida
|
|
const contentType = response.headers.get('content-type');
|
|
if (!contentType || !contentType.includes('application/json')) {
|
|
const textResponse = await response.text();
|
|
throw new Error(`Respuesta no válida del servidor: ${textResponse.substring(0, 200)}...`);
|
|
}
|
|
|
|
const data = await response.json();
|
|
|
|
// Agregar código de estado HTTP a la respuesta
|
|
data.httpStatus = response.status;
|
|
|
|
return data;
|
|
}
|
|
|
|
/**
|
|
* Validar formulario antes de enviar
|
|
*/
|
|
function validateForm(formData) {
|
|
const errors = [];
|
|
|
|
// Validaciones requeridas
|
|
const requiredFields = [
|
|
{ name: 'empresa', label: 'Nombre de la Empresa' },
|
|
{ name: 'usuario', label: 'Nombre de Usuario' },
|
|
{ name: 'email', label: 'Correo Electrónico' },
|
|
{ name: 'clavePublica', label: 'Clave Pública' },
|
|
{ name: 'clavePrivada', label: 'Clave Privada' }
|
|
];
|
|
|
|
requiredFields.forEach(field => {
|
|
const value = formData.get(field.name);
|
|
if (!value || value.trim() === '') {
|
|
errors.push(`${field.label} es obligatorio`);
|
|
}
|
|
});
|
|
|
|
// Validar formato de email
|
|
const email = formData.get('email');
|
|
if (email && !isValidEmail(email)) {
|
|
errors.push('El formato del correo electrónico no es válido');
|
|
}
|
|
|
|
// Validar usuario (solo alfanuméricos, guiones y guiones bajos)
|
|
const usuario = formData.get('usuario');
|
|
if (usuario && !/^[a-zA-Z0-9_-]+$/.test(usuario)) {
|
|
errors.push('El usuario solo puede contener letras, números, guiones y guiones bajos');
|
|
}
|
|
|
|
// Validar longitud mínima de claves
|
|
const clavePublica = formData.get('clavePublica');
|
|
const clavePrivada = formData.get('clavePrivada');
|
|
|
|
|
|
// Mostrar errores si los hay
|
|
if (errors.length > 0) {
|
|
showMessage('error', errors.join('<br>'));
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Manejar respuesta exitosa
|
|
*/
|
|
function handleSuccess(response) {
|
|
console.log('Usuario registrado exitosamente:', response);
|
|
|
|
// Mostrar mensaje de éxito
|
|
showMessage('success', response.message || 'Usuario registrado exitosamente');
|
|
const input = document.getElementById('apiKeyInput');
|
|
input.innerHTML = `Usuario, esta es su apikey, guardela: <br>`+response.data.apiKey;
|
|
|
|
// Limpiar formulario
|
|
form.reset();
|
|
|
|
// Opcional: Redireccionar después de unos segundos
|
|
// setTimeout(() => {
|
|
// window.location.href = '/dashboard/usuarios';
|
|
// }, 3000);
|
|
|
|
// Opcional: Actualizar lista de usuarios si existe
|
|
if (typeof window.updateUsersList === 'function') {
|
|
window.updateUsersList();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Manejar errores de la API
|
|
*/
|
|
function handleError(response) {
|
|
console.error('Error de la API:', response);
|
|
|
|
let errorMessage = 'Error al registrar usuario';
|
|
|
|
// Manejar diferentes tipos de errores
|
|
if (response.httpStatus === 422 && response.errors) {
|
|
// Errores de validación
|
|
const validationErrors = Object.values(response.errors).flat();
|
|
errorMessage = validationErrors.join('<br>');
|
|
} else if (response.httpStatus === 409) {
|
|
// Conflicto (usuario/email ya existe)
|
|
errorMessage = response.message || 'El usuario o email ya existe';
|
|
} else if (response.message) {
|
|
errorMessage = response.message;
|
|
} else if (response.httpStatus >= 500) {
|
|
errorMessage = 'Error interno del servidor. Por favor intente nuevamente.';
|
|
}
|
|
|
|
showMessage('error', errorMessage);
|
|
}
|
|
|
|
/**
|
|
* Manejar errores de red
|
|
*/
|
|
function handleNetworkError(error) {
|
|
console.error('Error de red:', error);
|
|
|
|
let errorMessage = 'Error de conexión';
|
|
|
|
if (error.name === 'TypeError' && error.message.includes('fetch')) {
|
|
errorMessage = 'No se pudo conectar al servidor. Verifique su conexión a internet.';
|
|
} else if (error.message.includes('JSON')) {
|
|
errorMessage = 'Error en la respuesta del servidor. Por favor contacte al administrador.';
|
|
} else {
|
|
errorMessage = `Error: ${error.message}`;
|
|
}
|
|
|
|
showMessage('error', errorMessage);
|
|
}
|
|
|
|
/**
|
|
* Mostrar mensajes al usuario
|
|
*/
|
|
function showMessage(type, message) {
|
|
// Crear modal si no existe
|
|
let modal = document.getElementById('messageModal');
|
|
if (!modal) {
|
|
modal = createMessageModal();
|
|
}
|
|
|
|
// Configurar contenido del modal
|
|
const modalTitle = modal.querySelector('.modal-title');
|
|
const modalMessage = modal.querySelector('.modal-message');
|
|
const modalIcon = modal.querySelector('.modal-icon');
|
|
const modalContent = modal.querySelector('.modal-content');
|
|
|
|
// Configurar según el tipo de mensaje
|
|
if (type === 'success') {
|
|
modalTitle.textContent = 'Éxito';
|
|
modalIcon.textContent = '✅';
|
|
modalContent.className = 'modal-content success';
|
|
} else {
|
|
modalTitle.textContent = 'Error';
|
|
modalIcon.textContent = '❌';
|
|
modalContent.className = 'modal-content error';
|
|
}
|
|
|
|
modalMessage.innerHTML = message;
|
|
|
|
// Mostrar modal con animación
|
|
modal.style.display = 'flex';
|
|
setTimeout(() => {
|
|
modal.classList.add('show');
|
|
}, 10);
|
|
|
|
// Enfocar el botón de cerrar para accesibilidad
|
|
const closeBtn = modal.querySelector('.modal-close-btn');
|
|
closeBtn.focus();
|
|
}
|
|
|
|
// Función auxiliar para crear el modal
|
|
function createMessageModal() {
|
|
const modal = document.createElement('div');
|
|
modal.id = 'messageModal';
|
|
modal.className = 'message-modal';
|
|
|
|
modal.innerHTML = `
|
|
<div class="modal-overlay">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<span class="modal-icon"></span>
|
|
<h3 class="modal-title"></h3>
|
|
</div>
|
|
<div class="modal-body">
|
|
<div class="modal-message"></div>
|
|
Esta es su clave de API:
|
|
<div class="api-key" id="apiKeyInput">
|
|
</div>
|
|
</div>
|
|
|
|
<div class="modal-footer">
|
|
<button type="button" class="modal-close-btn">Cerrar</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
`;
|
|
|
|
// Agregar estilos CSS
|
|
if (!document.getElementById('modalStyles')) {
|
|
const styles = document.createElement('style');
|
|
styles.id = 'modalStyles';
|
|
styles.textContent = `
|
|
.message-modal {
|
|
position: fixed;
|
|
top: 0;
|
|
left: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
z-index: 10000;
|
|
display: none;
|
|
opacity: 0;
|
|
transition: opacity 0.3s ease;
|
|
}
|
|
|
|
.message-modal.show {
|
|
opacity: 1;
|
|
}
|
|
|
|
.modal-overlay {
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
background: rgba(0, 0, 0, 0.5);
|
|
backdrop-filter: blur(4px);
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
padding: 1rem;
|
|
}
|
|
|
|
.modal-content {
|
|
background: white;
|
|
border-radius: 16px;
|
|
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.15);
|
|
max-width: 500px;
|
|
width: 100%;
|
|
max-height: 80vh;
|
|
overflow-y: auto;
|
|
transform: scale(0.9) translateY(20px);
|
|
transition: transform 0.3s ease;
|
|
}
|
|
|
|
.message-modal.show .modal-content {
|
|
transform: scale(1) translateY(0);
|
|
}
|
|
|
|
.modal-content.success {
|
|
border-top: 4px solid #48bb78;
|
|
}
|
|
|
|
.modal-content.error {
|
|
border-top: 4px solid #f56565;
|
|
}
|
|
|
|
.modal-header {
|
|
padding: 1.5rem 1.5rem 1rem 1.5rem;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 0.75rem;
|
|
}
|
|
|
|
.modal-icon {
|
|
font-size: 1.5rem;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.modal-title {
|
|
font-size: 1.25rem;
|
|
font-weight: 600;
|
|
margin: 0;
|
|
color: #2d3748;
|
|
}
|
|
|
|
.modal-body {
|
|
padding: 0 1.5rem 1rem 1.5rem;
|
|
}
|
|
|
|
.modal-message {
|
|
color: #4a5568;
|
|
line-height: 1.6;
|
|
font-size: 0.95rem;
|
|
}
|
|
|
|
.modal-footer {
|
|
padding: 1rem 1.5rem 1.5rem 1.5rem;
|
|
display: flex;
|
|
justify-content: flex-end;
|
|
}
|
|
|
|
.modal-close-btn {
|
|
background: linear-gradient(135deg, #4c51bf 0%, #667eea 100%);
|
|
color: white;
|
|
border: none;
|
|
padding: 0.75rem 1.5rem;
|
|
border-radius: 8px;
|
|
font-weight: 600;
|
|
cursor: pointer;
|
|
transition: all 0.2s ease;
|
|
font-size: 0.9rem;
|
|
}
|
|
|
|
.modal-close-btn:hover {
|
|
transform: translateY(-1px);
|
|
box-shadow: 0 4px 12px rgba(76, 81, 191, 0.3);
|
|
}
|
|
|
|
.modal-close-btn:active {
|
|
transform: translateY(0);
|
|
}
|
|
|
|
.modal-close-btn:focus {
|
|
outline: none;
|
|
box-shadow: 0 0 0 3px rgba(76, 81, 191, 0.3);
|
|
}
|
|
|
|
@media (max-width: 640px) {
|
|
.modal-overlay {
|
|
padding: 0.5rem;
|
|
}
|
|
|
|
.modal-content {
|
|
max-height: 90vh;
|
|
}
|
|
|
|
.modal-header,
|
|
.modal-body,
|
|
.modal-footer {
|
|
padding-left: 1rem;
|
|
padding-right: 1rem;
|
|
}
|
|
}
|
|
`;
|
|
document.head.appendChild(styles);
|
|
}
|
|
|
|
// Agregar al body
|
|
document.body.appendChild(modal);
|
|
|
|
// Configurar eventos
|
|
const closeBtn = modal.querySelector('.modal-close-btn');
|
|
const overlay = modal.querySelector('.modal-overlay');
|
|
|
|
// Cerrar con botón
|
|
closeBtn.addEventListener('click', () => closeModal(modal));
|
|
|
|
// Cerrar al hacer clic fuera del modal
|
|
overlay.addEventListener('click', (e) => {
|
|
if (e.target === overlay) {
|
|
closeModal(modal);
|
|
}
|
|
});
|
|
|
|
// Cerrar con Escape
|
|
document.addEventListener('keydown', (e) => {
|
|
if (e.key === 'Escape' && modal.classList.contains('show')) {
|
|
closeModal(modal);
|
|
}
|
|
});
|
|
|
|
return modal;
|
|
}
|
|
|
|
// Función para cerrar modal
|
|
function closeModal(modal) {
|
|
modal.classList.remove('show');
|
|
setTimeout(() => {
|
|
modal.style.display = 'none';
|
|
}, 300);
|
|
}
|
|
|
|
/**
|
|
* Validar formato de email
|
|
*/
|
|
function isValidEmail(email) {
|
|
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
return emailRegex.test(email);
|
|
}
|
|
|
|
/**
|
|
* Función para debugging (remover en producción)
|
|
*/
|
|
window.debugForm = function() {
|
|
const formData = new FormData(form);
|
|
console.log('Datos actuales del formulario:');
|
|
for (let [key, value] of formData.entries()) {
|
|
console.log(`${key}:`, value);
|
|
}
|
|
};
|
|
});
|
|
|
|
// ============================================================================
|
|
// CONFIGURACIÓN ADICIONAL OPCIONAL
|
|
// ============================================================================
|
|
|
|
// Si usas CSRF tokens en Laravel (para rutas web)
|
|
// Agregar en el <head> de tu HTML:
|
|
// <meta name="csrf-token" content="{{ csrf_token() }}">
|
|
|
|
// Si usas autenticación con tokens
|
|
// Los tokens se pueden almacenar en localStorage o sessionStorage:
|
|
// localStorage.setItem('auth_token', 'tu_token_aqui');
|
|
|
|
// Configuración global para todas las peticiones fetch
|
|
window.addEventListener('load', function() {
|
|
// Configurar interceptor para errores globales
|
|
const originalFetch = window.fetch;
|
|
window.fetch = function(...args) {
|
|
return originalFetch.apply(this, args)
|
|
.catch(error => {
|
|
console.error('Error global de fetch:', error);
|
|
throw error;
|
|
});
|
|
};
|
|
});
|
|
</script>
|
|
<?php include("footer.php");?>
|