559 lines
23 KiB
PHP
Executable File
559 lines
23 KiB
PHP
Executable File
<?php
|
|
require_once __DIR__ . '/includes/session_check.php';
|
|
require_once __DIR__ . '/includes/db.php';
|
|
require_once __DIR__ . '/includes/activity_logger.php';
|
|
|
|
$user_id = $_SESSION['user_id'];
|
|
|
|
// Handle form submissions
|
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|
// Action: Change Password
|
|
if (isset($_POST['change_own_password'])) {
|
|
$current_password = $_POST['current_password'];
|
|
$new_password = $_POST['new_password'];
|
|
$confirm_password = $_POST['confirm_password'];
|
|
|
|
if (empty($current_password) || empty($new_password) || empty($confirm_password)) {
|
|
$error = "Todos los campos de contraseña son obligatorios.";
|
|
} elseif ($new_password !== $confirm_password) {
|
|
$error = "Las contraseñas nuevas no coinciden.";
|
|
} else {
|
|
try {
|
|
$stmt = $pdo->prepare("SELECT password FROM users WHERE id = ?");
|
|
$stmt->execute([$user_id]);
|
|
$user = $stmt->fetch();
|
|
|
|
if ($user && password_verify($current_password, $user['password'])) {
|
|
$hashedPassword = password_hash($new_password, PASSWORD_DEFAULT);
|
|
$updateStmt = $pdo->prepare("UPDATE users SET password = ? WHERE id = ?");
|
|
$updateStmt->execute([$hashedPassword, $user_id]);
|
|
log_activity($user_id, 'Password Changed', 'User ' . $_SESSION['username'] . ' changed their own password.');
|
|
$success = "Tu contraseña ha sido actualizada con éxito.";
|
|
} else {
|
|
$error = "La contraseña actual es incorrecta.";
|
|
}
|
|
} catch (PDOException $e) {
|
|
$error = "Error al actualizar la contraseña.";
|
|
error_log($e->getMessage());
|
|
}
|
|
}
|
|
}
|
|
|
|
// Action: Link Telegram Account
|
|
if (isset($_POST['link_telegram'])) {
|
|
$telegram_chat_id = trim($_POST['telegram_chat_id']);
|
|
|
|
if (empty($telegram_chat_id)) {
|
|
// Si el campo está vacío, desvincular la cuenta
|
|
$stmt = $pdo->prepare("UPDATE users SET telegram_chat_id = NULL WHERE id = ?");
|
|
$stmt->execute([$user_id]);
|
|
log_activity($user_id, 'Telegram Unlinked', 'User ' . $_SESSION['username'] . ' unlinked their Telegram account.');
|
|
$success = "Cuenta de Telegram desvinculada con éxito.";
|
|
} elseif (!is_numeric($telegram_chat_id)) {
|
|
$error = "El ID de Chat de Telegram debe ser un número.";
|
|
} else {
|
|
try {
|
|
$stmt = $pdo->prepare("UPDATE users SET telegram_chat_id = ? WHERE id = ?");
|
|
$stmt->execute([$telegram_chat_id, $user_id]);
|
|
log_activity($user_id, 'Telegram Linked', 'User ' . $_SESSION['username'] . ' linked Telegram chat ID: ' . $telegram_chat_id);
|
|
$success = "ID de Chat de Telegram guardado con éxito.";
|
|
} catch (PDOException $e) {
|
|
$error = "Error al guardar el ID de Chat de Telegram.";
|
|
error_log($e->getMessage());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Fetch current user data
|
|
$stmt = $pdo->prepare("SELECT telegram_chat_id FROM users WHERE id = ?");
|
|
$stmt->execute([$user_id]);
|
|
$currentUser = $stmt->fetch(PDO::FETCH_ASSOC);
|
|
|
|
require_once __DIR__ . '/templates/header.php';
|
|
?>
|
|
|
|
<div class="container-fluid">
|
|
<h1 class="mt-4">Mi Perfil</h1>
|
|
|
|
<?php if (isset($error)): ?><div class="alert alert-danger"><?php echo $error; ?></div><?php endif; ?>
|
|
<?php if (isset($success)): ?><div class="alert alert-success"><?php echo $success; ?></div><?php endif; ?>
|
|
|
|
<div class="row">
|
|
<!-- Change Password Card -->
|
|
<div class="col-lg-6 mb-4">
|
|
<div class="card shadow-sm h-100">
|
|
<div class="card-header">
|
|
<h5 class="mb-0">Cambiar mi Contraseña</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<form action="" method="POST">
|
|
<div class="mb-3">
|
|
<label for="current_password" class="form-label">Contraseña Actual</label>
|
|
<input type="password" class="form-control" id="current_password" name="current_password" required>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="new_password" class="form-label">Nueva Contraseña</label>
|
|
<input type="password" class="form-control" id="new_password" name="new_password" required>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="confirm_password" class="form-label">Confirmar Nueva Contraseña</label>
|
|
<input type="password" class="form-control" id="confirm_password" name="confirm_password" required>
|
|
</div>
|
|
<button type="submit" name="change_own_password" class="btn btn-primary">Cambiar Contraseña</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Link Telegram Account Card -->
|
|
<div class="col-lg-6 mb-4">
|
|
<div class="card shadow-sm h-100">
|
|
<div class="card-header">
|
|
<h5 class="mb-0">Vincular Cuenta de Telegram</h5>
|
|
</div>
|
|
<div class="card-body d-flex flex-column">
|
|
<p class="text-muted">Vincula tu cuenta para poder enviar comandos al bot desde tu chat privado de Telegram.</p>
|
|
<form action="" method="POST" class="mt-auto">
|
|
<div class="mb-3">
|
|
<label for="telegram_chat_id" class="form-label">Tu ID de Chat de Telegram</label>
|
|
<input type="text" class="form-control" id="telegram_chat_id" name="telegram_chat_id"
|
|
value="<?= htmlspecialchars($currentUser['telegram_chat_id'] ?? '') ?>"
|
|
placeholder="Ej: 123456789">
|
|
<div class="form-text mt-2">
|
|
<strong>¿Cómo obtener tu ID?</strong>
|
|
<ol class="mb-0 ps-3">
|
|
<li>Abre Telegram y busca el bot <a href="https://t.me/userinfobot" target="_blank">@userinfobot</a>.</li>
|
|
<li>Inicia una conversación con él.</li>
|
|
<li>El bot te responderá inmediatamente con tu ID.</li>
|
|
</ol>
|
|
</div>
|
|
</div>
|
|
<button type="submit" name="link_telegram" class="btn btn-info text-white">Guardar ID de Telegram</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<?php if ($_SESSION['role'] === 'admin'): ?>
|
|
<!-- Admin Section -->
|
|
<hr class="my-4">
|
|
<h3 class="mt-4 mb-3">Panel de Administrador</h3>
|
|
|
|
<div class="row">
|
|
<!-- Upload Login Image Card -->
|
|
<div class="col-lg-6 mb-4">
|
|
<div class="card shadow-sm h-100">
|
|
<div class="card-header bg-primary text-white">
|
|
<h5 class="mb-0"><i class="bi bi-image me-2"></i>Cambiar Imagen de Login</h5>
|
|
</div>
|
|
<div class="card-body d-flex flex-column">
|
|
<p class="text-muted mb-3">Actualiza la imagen de fondo de la pantalla de inicio de sesión (Máximo 5MB).</p>
|
|
|
|
<!-- Current login image preview -->
|
|
<div class="mb-3">
|
|
<label class="form-label">Imagen Actual:</label>
|
|
<div class="border rounded p-2">
|
|
<img id="loginCurrentPreview" src="<?php echo site_url('galeria/login.png?t=' . time()); ?>" alt="Login Image" class="img-fluid" style="max-height: 150px; object-fit: cover;">
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Upload form -->
|
|
<form id="loginImageForm" class="mt-auto">
|
|
<div class="mb-3">
|
|
<label for="loginImage" class="form-label">Seleccionar nueva imagen:</label>
|
|
<input type="file" class="form-control" id="loginImage" name="image" accept="image/*" onchange="prepareImageEditor('login')">
|
|
<small class="form-text text-muted">Formatos: JPG, PNG, GIF, WebP</small>
|
|
</div>
|
|
|
|
<!-- Preview of selected image -->
|
|
<div id="loginPreviewSection" class="mb-3" style="display: none;">
|
|
<label class="form-label">Previsualización:</label>
|
|
<div class="border rounded p-2">
|
|
<img id="loginPreview" class="img-fluid" style="max-height: 150px; object-fit: cover;">
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Editor button -->
|
|
<button type="button" class="btn btn-secondary w-100 mb-2" id="loginEditorBtn" onclick="openImageEditor('login')" style="display: none;">
|
|
<i class="bi bi-crop me-2"></i>Ajustar Imagen
|
|
</button>
|
|
|
|
<button type="button" class="btn btn-primary w-100" id="loginUploadBtn" onclick="uploadAdminImage('login')">
|
|
<i class="bi bi-cloud-upload me-2"></i>Subir Imagen de Login
|
|
</button>
|
|
<div id="loginStatus" class="mt-2"></div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Upload Logo Card -->
|
|
<div class="col-lg-6 mb-4">
|
|
<div class="card shadow-sm h-100">
|
|
<div class="card-header bg-success text-white">
|
|
<h5 class="mb-0"><i class="bi bi-shield-check me-2"></i>Cambiar Logo</h5>
|
|
</div>
|
|
<div class="card-body d-flex flex-column">
|
|
<p class="text-muted mb-3">Actualiza el logo que se muestra en la navegación (Máximo 5MB).</p>
|
|
|
|
<!-- Current logo preview -->
|
|
<div class="mb-3">
|
|
<label class="form-label">Logo Actual:</label>
|
|
<div class="border rounded p-2" style="background-color: #f8f9fa;">
|
|
<img id="logoCurrentPreview" src="<?php echo site_url('assets/images/logo.png?t=' . time()); ?>" alt="Logo" style="max-height: 60px; object-fit: contain;">
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Upload form -->
|
|
<form id="logoImageForm" class="mt-auto">
|
|
<div class="mb-3">
|
|
<label for="logoImage" class="form-label">Seleccionar nuevo logo:</label>
|
|
<input type="file" class="form-control" id="logoImage" name="image" accept="image/*" onchange="prepareImageEditor('logo')">
|
|
<small class="form-text text-muted">Formatos: JPG, PNG, GIF, WebP</small>
|
|
</div>
|
|
|
|
<!-- Preview of selected image -->
|
|
<div id="logoPreviewSection" class="mb-3" style="display: none;">
|
|
<label class="form-label">Previsualización:</label>
|
|
<div class="border rounded p-2" style="background-color: #f8f9fa;">
|
|
<img id="logoPreview" style="max-height: 60px; object-fit: contain;">
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Editor button -->
|
|
<button type="button" class="btn btn-secondary w-100 mb-2" id="logoEditorBtn" onclick="openImageEditor('logo')" style="display: none;">
|
|
<i class="bi bi-crop me-2"></i>Ajustar Logo
|
|
</button>
|
|
|
|
<button type="button" class="btn btn-success w-100" id="logoUploadBtn" onclick="uploadAdminImage('logo')">
|
|
<i class="bi bi-cloud-upload me-2"></i>Subir Logo
|
|
</button>
|
|
<div id="logoStatus" class="mt-2"></div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<?php endif; ?>
|
|
</div>
|
|
|
|
<!-- JavaScript para upload de imágenes admin -->
|
|
<script src="https://cdn.jsdelivr.net/npm/cropperjs@1.5.13/dist/cropper.min.js"></script>
|
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/cropperjs@1.5.13/dist/cropper.min.css">
|
|
|
|
<style>
|
|
#imageEditorModal .modal-body {
|
|
max-height: 70vh;
|
|
overflow-y: auto;
|
|
}
|
|
|
|
#editorImage {
|
|
max-width: 100%;
|
|
max-height: 500px;
|
|
}
|
|
|
|
.editor-controls {
|
|
display: flex;
|
|
gap: 10px;
|
|
flex-wrap: wrap;
|
|
margin-bottom: 15px;
|
|
}
|
|
|
|
.editor-controls button {
|
|
flex: 1;
|
|
min-width: 100px;
|
|
font-size: 0.9rem;
|
|
}
|
|
</style>
|
|
|
|
<div class="modal fade" id="imageEditorModal" tabindex="-1">
|
|
<div class="modal-dialog modal-lg">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title">Ajustar Imagen</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<div id="editorContainer">
|
|
<img id="editorImage" src="">
|
|
</div>
|
|
<div class="editor-controls mt-3">
|
|
<button type="button" class="btn btn-sm btn-outline-secondary" onclick="rotateImage(-90)">
|
|
<i class="bi bi-arrow-counterclockwise me-1"></i>Girar Izq
|
|
</button>
|
|
<button type="button" class="btn btn-sm btn-outline-secondary" onclick="rotateImage(90)">
|
|
<i class="bi bi-arrow-clockwise me-1"></i>Girar Der
|
|
</button>
|
|
<button type="button" class="btn btn-sm btn-outline-secondary" onclick="flipImageH()">
|
|
<i class="bi bi-arrow-left-right me-1"></i>Voltear H
|
|
</button>
|
|
<button type="button" class="btn btn-sm btn-outline-secondary" onclick="flipImageV()">
|
|
<i class="bi bi-arrow-up-down me-1"></i>Voltear V
|
|
</button>
|
|
<button type="button" class="btn btn-sm btn-outline-secondary" onclick="resetImage()">
|
|
<i class="bi bi-arrow-counterclockwise me-1"></i>Resetear
|
|
</button>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label class="form-label">Zoom: <span id="zoomValue">100%</span></label>
|
|
<input type="range" class="form-range" id="zoomSlider" min="100" max="300" value="100" oninput="zoomImage(this.value)">
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancelar</button>
|
|
<button type="button" class="btn btn-primary" onclick="applyImageEdit()">Aplicar Cambios</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
let cropperInstance = null;
|
|
let currentImageType = null;
|
|
let editedImageBlobs = {}; // Guardar blobs editados
|
|
|
|
// Función para enviar logs al servidor
|
|
function sendLog(message, details = null) {
|
|
fetch('<?php echo site_url('log_frontend.php'); ?>', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json'
|
|
},
|
|
body: JSON.stringify({
|
|
type: 'info',
|
|
message: message,
|
|
details: details
|
|
})
|
|
}).catch(e => console.error('Error sending log:', e));
|
|
}
|
|
|
|
function prepareImageEditor(type) {
|
|
const fileInputId = type === 'login' ? 'loginImage' : 'logoImage';
|
|
const previewId = type === 'login' ? 'loginPreview' : 'logoPreview';
|
|
const previewSectionId = type === 'login' ? 'loginPreviewSection' : 'logoPreviewSection';
|
|
const editorBtnId = type === 'login' ? 'loginEditorBtn' : 'logoEditorBtn';
|
|
|
|
const fileInput = document.getElementById(fileInputId);
|
|
const previewImg = document.getElementById(previewId);
|
|
const previewSection = document.getElementById(previewSectionId);
|
|
const editorBtn = document.getElementById(editorBtnId);
|
|
const file = fileInput.files[0];
|
|
|
|
if (file) {
|
|
sendLog('Archivo seleccionado para ' + type, {name: file.name, size: file.size});
|
|
const reader = new FileReader();
|
|
reader.onload = function(e) {
|
|
previewImg.src = e.target.result;
|
|
previewSection.style.display = 'block';
|
|
editorBtn.style.display = 'block';
|
|
delete editedImageBlobs[type];
|
|
};
|
|
reader.readAsDataURL(file);
|
|
} else {
|
|
previewSection.style.display = 'none';
|
|
editorBtn.style.display = 'none';
|
|
}
|
|
}
|
|
|
|
function openImageEditor(type) {
|
|
sendLog('Abriendo editor para tipo: ' + type);
|
|
currentImageType = type;
|
|
const previewId = type === 'login' ? 'loginPreview' : 'logoPreview';
|
|
const previewImg = document.getElementById(previewId);
|
|
const editorImg = document.getElementById('editorImage');
|
|
|
|
editorImg.src = previewImg.src;
|
|
|
|
if (cropperInstance) {
|
|
cropperInstance.destroy();
|
|
}
|
|
|
|
setTimeout(() => {
|
|
cropperInstance = new Cropper(editorImg, {
|
|
aspectRatio: type === 'login' ? 16/9 : 1/1,
|
|
autoCropArea: 0.8,
|
|
responsive: true,
|
|
guides: true,
|
|
highlight: true,
|
|
cropBoxMovable: true,
|
|
cropBoxResizable: true,
|
|
toggleDragModeOnDblclick: true,
|
|
});
|
|
|
|
const modal = new bootstrap.Modal(document.getElementById('imageEditorModal'));
|
|
modal.show();
|
|
sendLog('Cropper iniciado para ' + type);
|
|
}, 100);
|
|
}
|
|
|
|
function rotateImage(degrees) {
|
|
if (cropperInstance) {
|
|
cropperInstance.rotate(degrees);
|
|
}
|
|
}
|
|
|
|
function flipImageH() {
|
|
if (cropperInstance) {
|
|
cropperInstance.scaleX(-cropperInstance.getData().scaleX || -1);
|
|
}
|
|
}
|
|
|
|
function flipImageV() {
|
|
if (cropperInstance) {
|
|
cropperInstance.scaleY(-cropperInstance.getData().scaleY || -1);
|
|
}
|
|
}
|
|
|
|
function resetImage() {
|
|
if (cropperInstance) {
|
|
cropperInstance.reset();
|
|
}
|
|
}
|
|
|
|
function zoomImage(value) {
|
|
if (cropperInstance) {
|
|
const zoom = (value - 100) / 100;
|
|
cropperInstance.setCanvasData({
|
|
left: 0,
|
|
top: 0,
|
|
width: cropperInstance.getCanvasData().width,
|
|
height: cropperInstance.getCanvasData().height
|
|
});
|
|
cropperInstance.zoom(zoom);
|
|
document.getElementById('zoomValue').textContent = value + '%';
|
|
}
|
|
}
|
|
|
|
function applyImageEdit() {
|
|
if (!cropperInstance) {
|
|
sendLog('ERROR: cropperInstance es null en applyImageEdit');
|
|
return;
|
|
}
|
|
|
|
sendLog('Aplicando cambios para tipo: ' + currentImageType);
|
|
|
|
const canvas = cropperInstance.getCroppedCanvas({
|
|
maxWidth: 4096,
|
|
maxHeight: 4096,
|
|
fillColor: '#fff',
|
|
imageSmoothingEnabled: true,
|
|
imageSmoothingQuality: 'high',
|
|
});
|
|
|
|
const previewId = currentImageType === 'login' ? 'loginPreview' : 'logoPreview';
|
|
const previewImg = document.getElementById(previewId);
|
|
|
|
// Actualizar preview
|
|
const canvasDataUrl = canvas.toDataURL('image/png');
|
|
previewImg.src = canvasDataUrl;
|
|
|
|
// Convertir canvas a blob y guardarlo
|
|
canvas.toBlob(blob => {
|
|
editedImageBlobs[currentImageType] = blob;
|
|
sendLog('Blob guardado para ' + currentImageType, {size: blob.size, type: blob.type});
|
|
|
|
const modal = bootstrap.Modal.getInstance(document.getElementById('imageEditorModal'));
|
|
if (modal) {
|
|
modal.hide();
|
|
}
|
|
}, 'image/png', 0.95);
|
|
}
|
|
|
|
function uploadAdminImage(type) {
|
|
sendLog('Iniciando upload para tipo: ' + type);
|
|
|
|
const statusId = type === 'login' ? 'loginStatus' : 'logoStatus';
|
|
const previewId = type === 'login' ? 'loginCurrentPreview' : 'logoCurrentPreview';
|
|
const fileInputId = type === 'login' ? 'loginImage' : 'logoImage';
|
|
|
|
let fileToUpload = null;
|
|
|
|
// Usar blob editado si existe, sino usar archivo original
|
|
if (editedImageBlobs[type]) {
|
|
sendLog('USANDO BLOB EDITADO para ' + type);
|
|
fileToUpload = new File([editedImageBlobs[type]], 'edited-image.png', { type: 'image/png' });
|
|
} else {
|
|
sendLog('USANDO ARCHIVO ORIGINAL para ' + type);
|
|
const fileInput = document.getElementById(fileInputId);
|
|
fileToUpload = fileInput.files[0];
|
|
}
|
|
|
|
if (!fileToUpload) {
|
|
sendLog('ERROR: No hay archivo para subir tipo ' + type);
|
|
showStatus(statusId, 'Por favor selecciona un archivo.', 'warning');
|
|
return;
|
|
}
|
|
|
|
sendLog('Archivo a subir para ' + type, {
|
|
name: fileToUpload.name,
|
|
size: fileToUpload.size,
|
|
type: fileToUpload.type
|
|
});
|
|
|
|
// Validar tamaño
|
|
if (fileToUpload.size > 5 * 1024 * 1024) {
|
|
showStatus(statusId, 'El archivo es demasiado grande. Máximo 5MB.', 'danger');
|
|
return;
|
|
}
|
|
|
|
// Validar tipo
|
|
if (!fileToUpload.type.startsWith('image/')) {
|
|
showStatus(statusId, 'El archivo debe ser una imagen.', 'danger');
|
|
return;
|
|
}
|
|
|
|
const formData = new FormData();
|
|
formData.append('image', fileToUpload);
|
|
formData.append('type', type);
|
|
|
|
const buttonId = type === 'login' ? 'loginUploadBtn' : 'logoUploadBtn';
|
|
const button = document.getElementById(buttonId);
|
|
button.disabled = true;
|
|
button.innerHTML = '<span class="spinner-border spinner-border-sm me-2"></span>Subiendo...';
|
|
|
|
fetch('<?php echo site_url('upload_admin_images.php'); ?>', {
|
|
method: 'POST',
|
|
body: formData
|
|
})
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
sendLog('Respuesta del servidor para ' + type, data);
|
|
if (data.success) {
|
|
showStatus(statusId, data.message, 'success');
|
|
document.getElementById(previewId).src = data.url + '?t=' + Date.now();
|
|
|
|
const fileInput = document.getElementById(fileInputId);
|
|
fileInput.value = '';
|
|
|
|
document.getElementById(type === 'login' ? 'loginPreviewSection' : 'logoPreviewSection').style.display = 'none';
|
|
document.getElementById(type === 'login' ? 'loginEditorBtn' : 'logoEditorBtn').style.display = 'none';
|
|
|
|
delete editedImageBlobs[type];
|
|
|
|
sendLog('Upload completado exitosamente para ' + type);
|
|
setTimeout(() => location.reload(), 2000);
|
|
} else {
|
|
sendLog('ERROR en respuesta del servidor para ' + type, data);
|
|
showStatus(statusId, data.message, 'danger');
|
|
}
|
|
})
|
|
.catch(error => {
|
|
sendLog('ERROR en fetch para ' + type, {message: error.message});
|
|
showStatus(statusId, 'Error al subir la imagen: ' + error.message, 'danger');
|
|
})
|
|
.finally(() => {
|
|
button.disabled = false;
|
|
button.innerHTML = '<i class="bi bi-cloud-upload me-2"></i>Subir ' + (type === 'login' ? 'Imagen de Login' : 'Logo');
|
|
});
|
|
}
|
|
|
|
function showStatus(statusId, message, type) {
|
|
const statusDiv = document.getElementById(statusId);
|
|
const alertClass = 'alert-' + type;
|
|
statusDiv.innerHTML = '<div class="alert ' + alertClass + ' mb-0" role="alert">' + message + '</div>';
|
|
}
|
|
</script>
|
|
|
|
<?php require_once __DIR__ . '/templates/footer.php'; ?>
|