412 lines
14 KiB
PHP
Executable File
412 lines
14 KiB
PHP
Executable File
<?php
|
|
session_start();
|
|
|
|
ini_set('display_errors', 1);
|
|
ini_set('display_startup_errors', 1);
|
|
error_reporting(E_ALL);
|
|
|
|
// Cargar variables de entorno
|
|
if (file_exists(__DIR__ . '/../.env')) {
|
|
$lines = file(__DIR__ . '/../.env', FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
|
|
foreach ($lines as $line) {
|
|
if (strpos(trim($line), '#') === 0) continue;
|
|
if (strpos($line, '=') === false) continue;
|
|
list($key, $value) = explode('=', $line, 2);
|
|
$_ENV[trim($key)] = trim($value);
|
|
}
|
|
}
|
|
|
|
require_once __DIR__ . '/../shared/auth/jwt.php';
|
|
$userData = JWTAuth::requireAuth();
|
|
?>
|
|
<!DOCTYPE html>
|
|
<html lang="<?php echo $userData->idioma ?? 'es'; ?>">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Galería de Imágenes - Sistema de Bots</title>
|
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
|
<style>
|
|
:root {
|
|
--primary-color: #5865F2;
|
|
--success-color: #28a745;
|
|
--danger-color: #dc3545;
|
|
}
|
|
|
|
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
|
|
body {
|
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
background: #f0f2f5;
|
|
min-height: 100vh;
|
|
padding: 20px;
|
|
}
|
|
|
|
.header {
|
|
background: white;
|
|
border-radius: 15px;
|
|
padding: 20px 30px;
|
|
margin-bottom: 30px;
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
box-shadow: 0 5px 20px rgba(0, 0, 0, 0.1);
|
|
}
|
|
|
|
.header h1 { color: var(--primary-color); font-size: 24px; }
|
|
|
|
.container { max-width: 1400px; margin: 0 auto; }
|
|
|
|
.card {
|
|
background: white;
|
|
border-radius: 15px;
|
|
padding: 25px;
|
|
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
|
|
margin-bottom: 20px;
|
|
}
|
|
|
|
.btn {
|
|
padding: 10px 20px;
|
|
border-radius: 8px;
|
|
text-decoration: none;
|
|
border: none;
|
|
cursor: pointer;
|
|
font-weight: 600;
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
transition: all 0.2s;
|
|
}
|
|
.btn-primary { background: var(--primary-color); color: white; }
|
|
.btn-secondary { background: #6c757d; color: white; }
|
|
.btn-success { background: var(--success-color); color: white; }
|
|
.btn-danger { background: var(--danger-color); color: white; }
|
|
|
|
.upload-area {
|
|
border: 3px dashed #ddd;
|
|
border-radius: 10px;
|
|
padding: 40px;
|
|
text-align: center;
|
|
cursor: pointer;
|
|
transition: all 0.2s;
|
|
margin-bottom: 30px;
|
|
}
|
|
|
|
.upload-area:hover {
|
|
border-color: var(--primary-color);
|
|
background: #f8f9fa;
|
|
}
|
|
|
|
.gallery-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
|
gap: 20px;
|
|
}
|
|
|
|
.gallery-item {
|
|
background: #f8f9fa;
|
|
border-radius: 10px;
|
|
overflow: hidden;
|
|
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
|
|
transition: all 0.2s;
|
|
}
|
|
|
|
.gallery-item:hover {
|
|
transform: translateY(-5px);
|
|
box-shadow: 0 5px 15px rgba(0,0,0,0.15);
|
|
}
|
|
|
|
.gallery-item img {
|
|
width: 100%;
|
|
height: 150px;
|
|
object-fit: cover;
|
|
}
|
|
|
|
.gallery-item-info {
|
|
padding: 10px;
|
|
}
|
|
|
|
.gallery-item-name {
|
|
font-size: 14px;
|
|
font-weight: 600;
|
|
margin-bottom: 5px;
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
}
|
|
|
|
.gallery-item-meta {
|
|
font-size: 12px;
|
|
color: #666;
|
|
margin-bottom: 10px;
|
|
}
|
|
|
|
.gallery-item-actions {
|
|
display: flex;
|
|
gap: 5px;
|
|
}
|
|
|
|
.modal {
|
|
display: none;
|
|
position: fixed;
|
|
z-index: 1000;
|
|
left: 0;
|
|
top: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
background: rgba(0,0,0,0.5);
|
|
}
|
|
|
|
.modal-content {
|
|
background: white;
|
|
margin: 10% auto;
|
|
padding: 25px;
|
|
width: 90%;
|
|
max-width: 500px;
|
|
border-radius: 15px;
|
|
}
|
|
|
|
.form-control {
|
|
width: 100%;
|
|
padding: 10px;
|
|
border: 2px solid #eee;
|
|
border-radius: 8px;
|
|
margin: 10px 0;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="header">
|
|
<h1><i class="fas fa-images"></i> Galería de Imágenes</h1>
|
|
<a href="/index.php" class="btn btn-secondary">
|
|
<i class="fas fa-arrow-left"></i> Volver
|
|
</a>
|
|
</div>
|
|
|
|
<div class="container">
|
|
<div class="card">
|
|
<div class="upload-area" onclick="document.getElementById('fileInput').click()">
|
|
<i class="fas fa-cloud-upload-alt" style="font-size: 48px; color: var(--primary-color); margin-bottom: 15px;"></i>
|
|
<h3>Arrastra imágenes aquí o haz clic para seleccionar</h3>
|
|
<p style="color: #666; margin-top: 10px;">Formatos permitidos: JPG, PNG, GIF, WEBP (Máx. 5MB)</p>
|
|
</div>
|
|
<input type="file" id="fileInput" accept="image/*" multiple style="display: none;">
|
|
|
|
<div id="uploadProgress" style="display: none; margin-top: 20px;">
|
|
<div style="background: #f8f9fa; padding: 10px; border-radius: 8px;">
|
|
<div id="progressText">Subiendo...</div>
|
|
<div style="background: #ddd; height: 20px; border-radius: 10px; margin-top: 10px; overflow: hidden;">
|
|
<div id="progressBar" style="background: var(--success-color); height: 100%; width: 0%; transition: width 0.3s;"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="card">
|
|
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px;">
|
|
<h2 style="margin: 0;">Mis Imágenes</h2>
|
|
<input type="text" id="searchInput" placeholder="Buscar..." class="form-control" style="max-width: 300px; margin: 0;">
|
|
</div>
|
|
|
|
<div id="galleryContainer" class="gallery-grid">
|
|
<p style="text-align: center; color: #666;">Cargando...</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Modal Editar -->
|
|
<div id="editModal" class="modal">
|
|
<div class="modal-content">
|
|
<h2>Editar Imagen</h2>
|
|
<input type="text" id="editName" class="form-control" placeholder="Nombre de la imagen">
|
|
<div style="display: flex; gap: 10px; margin-top: 20px;">
|
|
<button class="btn btn-success" onclick="saveEdit()">Guardar</button>
|
|
<button class="btn btn-secondary" onclick="closeEditModal()">Cancelar</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
let currentEditId = null;
|
|
|
|
// Subir archivos
|
|
const fileInput = document.getElementById('fileInput');
|
|
const uploadArea = document.querySelector('.upload-area');
|
|
|
|
fileInput.addEventListener('change', handleFiles);
|
|
|
|
uploadArea.addEventListener('dragover', (e) => {
|
|
e.preventDefault();
|
|
uploadArea.style.borderColor = 'var(--primary-color)';
|
|
});
|
|
|
|
uploadArea.addEventListener('dragleave', () => {
|
|
uploadArea.style.borderColor = '#ddd';
|
|
});
|
|
|
|
uploadArea.addEventListener('drop', (e) => {
|
|
e.preventDefault();
|
|
uploadArea.style.borderColor = '#ddd';
|
|
fileInput.files = e.dataTransfer.files;
|
|
handleFiles();
|
|
});
|
|
|
|
async function handleFiles() {
|
|
const files = fileInput.files;
|
|
if (!files.length) return;
|
|
|
|
const progressDiv = document.getElementById('uploadProgress');
|
|
const progressBar = document.getElementById('progressBar');
|
|
const progressText = document.getElementById('progressText');
|
|
|
|
progressDiv.style.display = 'block';
|
|
|
|
for (let i = 0; i < files.length; i++) {
|
|
const file = files[i];
|
|
progressText.textContent = `Subiendo ${i + 1}/${files.length}: ${file.name}`;
|
|
progressBar.style.width = ((i / files.length) * 100) + '%';
|
|
|
|
const formData = new FormData();
|
|
formData.append('image', file);
|
|
|
|
try {
|
|
const response = await fetch('/gallery/api/upload.php', {
|
|
method: 'POST',
|
|
body: formData
|
|
});
|
|
const result = await response.json();
|
|
|
|
if (!result.success) {
|
|
alert('Error subiendo ' + file.name + ': ' + result.error);
|
|
}
|
|
} catch (error) {
|
|
alert('Error subiendo ' + file.name);
|
|
}
|
|
}
|
|
|
|
progressBar.style.width = '100%';
|
|
progressText.textContent = '¡Completado!';
|
|
|
|
setTimeout(() => {
|
|
progressDiv.style.display = 'none';
|
|
loadGallery();
|
|
fileInput.value = '';
|
|
}, 1000);
|
|
}
|
|
|
|
// Cargar galería
|
|
async function loadGallery(search = '') {
|
|
const container = document.getElementById('galleryContainer');
|
|
container.innerHTML = '<p style="text-align: center; color: #666;">Cargando...</p>';
|
|
|
|
try {
|
|
const url = '/gallery/api/list.php' + (search ? `?search=${encodeURIComponent(search)}` : '');
|
|
const response = await fetch(url);
|
|
const data = await response.json();
|
|
|
|
if (!data.success || !data.images.length) {
|
|
container.innerHTML = '<p style="text-align: center; color: #666;">No hay imágenes</p>';
|
|
return;
|
|
}
|
|
|
|
container.innerHTML = '';
|
|
data.images.forEach(img => {
|
|
const div = document.createElement('div');
|
|
div.className = 'gallery-item';
|
|
div.innerHTML = `
|
|
<img src="${img.url_thumbnail}" alt="${img.nombre_original}">
|
|
<div class="gallery-item-info">
|
|
<div class="gallery-item-name" title="${img.nombre_original}">${img.nombre_original}</div>
|
|
<div class="gallery-item-meta">
|
|
${Math.round(img.tamano / 1024)} KB • ${img.ancho}x${img.alto}
|
|
</div>
|
|
<div class="gallery-item-actions">
|
|
<button class="btn btn-primary" style="font-size: 11px; padding: 5px 10px;" onclick="openEditModal(${img.id}, '${img.nombre_original.replace(/'/g, "\\'")}')">
|
|
<i class="fas fa-edit"></i>
|
|
</button>
|
|
<button class="btn btn-danger" style="font-size: 11px; padding: 5px 10px;" onclick="deleteImage(${img.id})">
|
|
<i class="fas fa-trash"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
`;
|
|
container.appendChild(div);
|
|
});
|
|
} catch (error) {
|
|
container.innerHTML = '<p style="text-align: center; color: red;">Error cargando imágenes</p>';
|
|
}
|
|
}
|
|
|
|
function openEditModal(id, name) {
|
|
currentEditId = id;
|
|
document.getElementById('editName').value = name;
|
|
document.getElementById('editModal').style.display = 'block';
|
|
}
|
|
|
|
function closeEditModal() {
|
|
document.getElementById('editModal').style.display = 'none';
|
|
currentEditId = null;
|
|
}
|
|
|
|
async function saveEdit() {
|
|
const newName = document.getElementById('editName').value;
|
|
if (!newName) return;
|
|
|
|
try {
|
|
const response = await fetch('/gallery/api/edit.php', {
|
|
method: 'POST',
|
|
headers: {'Content-Type': 'application/json'},
|
|
body: JSON.stringify({
|
|
id: currentEditId,
|
|
nombre_original: newName
|
|
})
|
|
});
|
|
const result = await response.json();
|
|
|
|
if (result.success) {
|
|
closeEditModal();
|
|
loadGallery();
|
|
} else {
|
|
alert('Error: ' + result.error);
|
|
}
|
|
} catch (error) {
|
|
alert('Error de conexión');
|
|
}
|
|
}
|
|
|
|
async function deleteImage(id) {
|
|
if (!confirm('¿Eliminar esta imagen?')) return;
|
|
|
|
try {
|
|
const response = await fetch('/gallery/api/delete.php', {
|
|
method: 'POST',
|
|
headers: {'Content-Type': 'application/json'},
|
|
body: JSON.stringify({id: id})
|
|
});
|
|
const result = await response.json();
|
|
|
|
if (result.success) {
|
|
loadGallery();
|
|
} else {
|
|
alert('Error: ' + result.error);
|
|
}
|
|
} catch (error) {
|
|
alert('Error de conexión');
|
|
}
|
|
}
|
|
|
|
// Buscador
|
|
let searchTimeout;
|
|
document.getElementById('searchInput').addEventListener('input', (e) => {
|
|
clearTimeout(searchTimeout);
|
|
searchTimeout = setTimeout(() => {
|
|
loadGallery(e.target.value);
|
|
}, 500);
|
|
});
|
|
|
|
// Cargar al inicio
|
|
loadGallery();
|
|
</script>
|
|
</body>
|
|
</html>
|