Files
hmngy_pasarela_Frontend/editarUsuario.php

896 lines
30 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
}
// Obtener ID del usuario a editar
$userId = $_GET['id'] ?? null;
$userData = null;
if ($userId) {
// Aquí deberíamos obtener los datos del usuario de la API
// Como no tengo acceso directo a la BD, voy a simular una llamada o usar la lista de usuarios si es posible
// Por ahora, haré una llamada a la API de verUsuarios y filtraré (ineficiente pero funcional con lo que tengo)
$url = $ruta.'/api/verUsuarios';
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 0,
CURLOPT_CONNECTTIMEOUT => 0,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
));
$response = curl_exec($curl);
curl_close($curl);
$users = json_decode($response, true);
if ($users) {
foreach ($users as $user) {
if ($user['id'] == $userId) {
$userData = $user;
break;
}
}
}
}
if (!$userData) {
echo "<script>alert('Usuario no encontrado'); window.location.href='verUsuarios.php';</script>";
exit;
}
?>
<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.color-group {
display: flex;
align-items: center;
gap: 10px;
padding: 0.5rem;
}
.form-input.color-group input[type="color"] {
width: 50px;
height: 40px;
padding: 0;
border: 1px solid #e2e8f0;
border-radius: 6px;
}
.form-input.color-group input[type="text"] {
flex: 1;
border: 1px solid #e2e8f0;
border-radius: 6px;
padding: 0.5rem;
font-family: monospace;
}
.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;
}
.color-input {
display: flex;
align-items: center;
border: 1px solid #ccc;
border-radius: 6px;
padding: 5px;
width: 160px;
}
.color-input input[type="color"] {
border: none;
background: none;
width: 30px;
height: 30px;
padding: 0;
margin-right: 5px;
cursor: pointer;
}
.color-input input[type="text"] {
border: none;
outline: none;
width: 100%;
font-family: monospace;
}
</style>
<main class="app-main">
<!--begin::App Content Header-->
<div class="main-content">
<div class="form-container">
<h1 class="form-title">Editar Usuario</h1>
<p class="form-subtitle">Modifique los datos del usuario</p>
<form id="userForm" method="POST">
<input type="hidden" name="id" value="<?= htmlspecialchars($userData['id']) ?>">
<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
value="<?= htmlspecialchars($userData['empresa'] ?? '') ?>"
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
value="<?= htmlspecialchars($userData['usuario'] ?? '') ?>"
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
value="<?= htmlspecialchars($userData['email'] ?? '') ?>"
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" <?= ($userData['platf_pago'] ?? '') == 'stripe' ? 'selected' : '' ?>>Stripe</option>
<option value="openpay" <?= ($userData['platf_pago'] ?? '') == 'openpay' ? 'selected' : '' ?>>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-----&#10;MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA...&#10;-----END PUBLIC KEY-----"><?= htmlspecialchars($userData['clave_publica'] ?? '') ?></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-----&#10;MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC...&#10;-----END PRIVATE KEY-----"><?= htmlspecialchars($userData['clave_privada'] ?? '') ?></textarea>
</div>
<div class="form-group">
<label for="logo" class="form-label">Logo (opcional) — JPG, PNG o SVG, max 2MB</label>
<div class="file-input-wrapper">
<input type="file" id="logo" name="logo" accept="image/png,image/jpeg,image/jpg,image/svg+xml" class="file-input">
<label for="logo" id="logoLabel" class="file-input-label">
<?php echo !empty($userData['logo']) ? htmlspecialchars($userData['logo']) : 'Seleccione un archivo'; ?>
</label>
<?php if (!empty($userData['logo'])): ?>
<input type="hidden" name="current_logo" value="<?= htmlspecialchars($userData['logo']) ?>">
<?php endif; ?>
</div>
<?php if (!empty($userData['logo'])): ?>
<div style="margin-top: 10px;">
<small>Logo actual: <a href="<?=$ruta."/img/".htmlspecialchars($userData['logo']) ?>" target="_blank">Ver logo</a></small>
</div>
<?php endif; ?>
</div>
<div class="form-group">
<label for="brand" class="form-label">Selecciona el color del branding <small>(ocupa el selector de color)</small></label>
<div class="form-input color-group">
<input type="color" id="colorPicker" value="<?= htmlspecialchars($userData['colorBrand'] ?? '#0072ff') ?>">
<input type="text" id="colorValue" name="colorValue" value="<?= htmlspecialchars($userData['colorBrand'] ?? '#0072ff') ?>">
</div>
</div>
<button type="submit" class="submit-btn">
💾 Guardar Cambios
</button>
</form>
</div>
</div>
<!--end::App Main-->
<div id="toast" class="toast"></div>
</main>
<script>
const picker = document.getElementById('colorPicker');
const value = document.getElementById('colorValue');
picker.addEventListener('input', () => {
value.value = picker.value;
});
value.addEventListener('input', () => {
if (/^#[0-9A-Fa-f]{6}$/.test(value.value)) {
picker.value = value.value;
}
});
</script>
<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_edit.php', // Usamos el mismo endpoint
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 formato de email
*/
function isValidEmail(email) {
const re = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
return re.test(String(email).toLowerCase());
}
/**
* 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`);
}
});
const id = formData.get('id');
if (!id || String(id).trim() === '') {
errors.push('ID de Usuario 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');
// Validación de logo (opcional)
const logo = formData.get('logo');
if (logo && typeof logo === 'object' && logo.size && logo.name) {
const allowedTypes = ['image/jpeg', 'image/png', 'image/jpg', 'image/svg+xml'];
const maxSize = 2 * 1024 * 1024; // 2MB
if (!allowedTypes.includes(logo.type)) {
errors.push('El logo debe ser un archivo JPG, PNG o SVG');
}
if (logo.size > maxSize) {
errors.push('El logo no puede ser mayor a 2MB');
}
}
// Mostrar errores si los hay
if (errors.length > 0) {
showMessage('error', errors.join('<br>'));
return false;
}
return true;
}
// Mostrar nombre de archivo seleccionado y cambiar estilo
const logoInput = document.getElementById('logo');
const logoLabel = document.getElementById('logoLabel');
if (logoInput) {
logoInput.addEventListener('change', (e) => {
const file = e.target.files[0];
if (file) {
logoLabel.textContent = `${file.name} (${Math.round(file.size/1024)} KB)`;
logoLabel.classList.add('has-file');
} else {
logoLabel.textContent = 'Seleccione un archivo';
logoLabel.classList.remove('has-file');
}
});
}
/**
* Manejar respuesta exitosa
*/
function handleSuccess(response) {
console.log('Usuario actualizado exitosamente:', response);
// Mostrar mensaje de éxito
showMessage('success', response.message || 'Usuario actualizado exitosamente');
// Opcional: Redireccionar después de unos segundos
setTimeout(() => {
window.location.href = 'verUsuarios.php';
}, 2000);
}
/**
* Manejar errores de la API
*/
function handleError(response) {
console.error('Error de la API:', response);
let errorMessage = 'Error al actualizar 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>
</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: 1rem;
}
}
`;
document.head.appendChild(styles);
}
// Manejar cierre del modal
const closeBtn = modal.querySelector('.modal-close-btn');
const overlay = modal.querySelector('.modal-overlay');
function closeModal() {
modal.classList.remove('show');
setTimeout(() => {
modal.style.display = 'none';
}, 300);
}
closeBtn.onclick = closeModal;
overlay.onclick = (e) => {
if (e.target === overlay) closeModal();
};
// Cerrar con tecla Escape
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape' && modal.style.display === 'flex') {
closeModal();
}
});
document.body.appendChild(modal);
return modal;
} // <-- Esta llave cierra createMessageModal()
}); // <-- Esta llave cierra DOMContentLoaded
</script>
<?php include("footer.php");?>