<!DOCTYPE html> <html lang="es"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Visualizador de Documentos Anexados</title> <link rel="icon" type="image/png" href="assets/img/icono.png"> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet"> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> <style> body { background:linear-gradient(225deg,#d0101d,#752b2a); min-height:100vh; padding:20px; } .doc-container { background:#fff; border-radius:20px; max-width:960px; width:100%; padding:40px; box-shadow:10px 10px 20px 0 rgba(0,0,0,.3); margin:0 auto; } .document-card { border:2px solid #d9534f; border-radius:10px; padding:14px; margin-bottom:12px; transition:all .25s ease; animation:fadeIn .3s ease; } .document-card:hover { transform:translateY(-3px); box-shadow:0 8px 20px rgba(0,0,0,.1); } .document-card.selected { background:#e3f2fd; border-color:#2196f3; } .btn-view { background:#5bc0de; color:#fff; border:none; } .btn-download { background:#d0101d; color:#fff; border:none; } .btn-delete { background:#6c757d; color:#fff; border:none; } .btn-view:hover,.btn-download:hover,.btn-delete:hover { opacity:.8; } .stats-badge { background:#979393; color:#fff; padding:5px 12px; border-radius:15px; font-size:.85em; margin-left:8px; } .search-stats { background:#f8f9fa; padding:10px 15px; border-radius:8px; margin-bottom:15px; border-left:4px solid #007bff; } .date-filter { background:#f8f9fa; padding:15px; border-radius:10px; margin-bottom:20px; border-left:4px solid #28a745; } .no-results { display:none; text-align:center; padding:40px; color:#666; background:#f8f9fa; border-radius:10px; } .highlight { background:#ffeb3b; font-weight:bold; padding:2px 4px; border-radius:3px; } .pagination-wrap { display:flex; justify-content:center; gap:15px; margin-top:25px; align-items:center; } .pag-info { background:#f8f9fa; padding:7px 14px; border-radius:20px; font-size:.9em; color:#666; } @keyframes fadeIn { from{opacity:0;transform:translateY(8px)} to{opacity:1;transform:translateY(0)} } </style> </head> <body> <div class="container"> <div class="doc-container"> <img src="assets/img/LOGO.png" alt="" style="max-width:220px;display:block;margin:0 auto 1.5rem;" onerror="this.style.display='none'"> <?php // Helper de iconos function iconForExt($archivo) { $ext = strtolower(pathinfo($archivo, PATHINFO_EXTENSION)); $map = ['pdf'=>'fa-file-pdf text-danger','jpg'=>'fa-file-image text-success', 'jpeg'=>'fa-file-image text-success','png'=>'fa-file-image text-success', 'gif'=>'fa-file-image text-success','txt'=>'fa-file-alt text-info', 'doc'=>'fa-file-word text-primary','docx'=>'fa-file-word text-primary', 'xls'=>'fa-file-excel text-success','xlsx'=>'fa-file-excel text-success']; return 'fas ' . ($map[$ext] ?? 'fa-file text-secondary'); } function puedeVisualizar($archivo) { $ext = strtolower(pathinfo($archivo, PATHINFO_EXTENSION)); return in_array($ext, ['pdf','jpg','jpeg','png','gif','txt']); } ?> <h2 class="text-center"> Documentos Anexados <span class="stats-badge" id="totalStats"> <i class="fas fa-file-alt"></i> <?= $totalDocuments ?> documentos </span> </h2> <hr> <!-- Alertas flash --> <?php if (!empty($_SESSION['anexo_ok'])): ?> <div class="alert alert-success"><i class="fas fa-check-circle me-2"></i><?= $_SESSION['anexo_ok'] ?></div> <?php unset($_SESSION['anexo_ok']); ?> <?php endif; ?> <!-- Botones superiores --> <div class="mb-4 d-flex gap-2 flex-wrap"> <a href="index.php?controller=Anexo&action=index" class="btn btn-danger"> <i class="fas fa-upload me-1"></i>Subir Documento </a> <a href="javascript:history.back()" class="btn btn-warning"> <i class="fas fa-undo me-1"></i>Regresar </a> <a href="index.php?controller=Dashboard&action=index" class="btn btn-secondary"> <i class="fas fa-house me-1"></i>Menú </a> </div> <!-- Filtro por fecha y búsqueda --> <form method="GET" action="index.php" class="date-filter"> <input type="hidden" name="controller" value="Anexo"> <input type="hidden" name="action" value="visualizar"> <div class="row align-items-end g-3"> <div class="col-md-4"> <label class="form-label"><i class="fas fa-calendar-alt me-1"></i>Filtrar por fecha:</label> <input type="date" name="date" class="form-control" value="<?= htmlspecialchars($dateFilter) ?>"> </div> <div class="col-md-4"> <label class="form-label"><i class="fas fa-search me-1"></i>Buscar por nombre:</label> <input type="text" name="search" class="form-control" placeholder="Nombre del archivo..." value="<?= htmlspecialchars($searchTerm) ?>"> </div> <div class="col-md-4 d-flex gap-2 align-items-end"> <button type="submit" class="btn btn-primary flex-grow-1"> <i class="fas fa-filter me-1"></i>Filtrar </button> <a href="index.php?controller=Anexo&action=visualizar" class="btn btn-outline-secondary"> <i class="fas fa-times"></i> </a> </div> </div> <?php if ($dateFilter || $searchTerm): ?> <div class="mt-2"> <small class="text-muted">Filtros activos: <?php if ($dateFilter): ?><span class="badge bg-info">Fecha: <?= htmlspecialchars($dateFilter) ?></span><?php endif; ?> <?php if ($searchTerm): ?><span class="badge bg-info">Búsqueda: "<?= htmlspecialchars($searchTerm) ?>"</span><?php endif; ?> </small> </div> <?php endif; ?> </form> <!-- Búsqueda en tiempo real (cliente) --> <div class="mb-3"> <div class="input-group"> <input type="text" id="searchInput" class="form-control" placeholder="Buscar en tiempo real..." autocomplete="off"> <button class="btn btn-outline-secondary" id="clearSearch" style="display:none"> <i class="fas fa-times"></i> </button> <button class="btn btn-outline-secondary" id="searchButton"> <i class="fas fa-search"></i> </button> </div> <small class="text-muted mt-1 d-block"><i class="fas fa-keyboard me-1"></i><strong>Escape</strong> para limpiar</small> </div> <div class="search-stats" id="searchStats"> <i class="fas fa-chart-bar me-1"></i> Mostrando <span id="resultsCount"><?= count($currentPageFiles) ?></span> de <span id="totalDocuments"><?= $totalDocuments ?></span> documentos <?php if ($totalPages > 1): ?> — Página <?= $page ?> de <?= $totalPages ?><?php endif; ?> </div> <div id="noResults" class="no-results"> <i class="fas fa-search fa-3x mb-3" style="color:#ddd"></i> <h5>No se encontraron resultados</h5> </div> <!-- Tarjetas de documentos --> <div class="row" id="documentsContainer"> <?php if (!empty($currentPageFiles)): ?> <?php foreach ($currentPageFiles as $fi): $archivo = $fi['name']; $ruta = $fi['path']; $icono = iconForExt($archivo); $ver = puedeVisualizar($archivo); ?> <div class="col-md-6 document-item" data-filename="<?= strtolower(htmlspecialchars($archivo)) ?>" data-date="<?= htmlspecialchars($fi['date']) ?>"> <div class="document-card" onclick="selectDoc(this)"> <div class="d-flex justify-content-between align-items-center gap-2"> <div class="text-truncate flex-grow-1"> <i class="<?= $icono ?> me-1"></i> <span class="fw-bold document-name" title="<?= htmlspecialchars($archivo) ?>"> <?= htmlspecialchars($archivo) ?> </span> <br> <small class="text-muted"><i class="fas fa-clock me-1"></i><?= $fi['date'] ?></small> </div> <div class="btn-group btn-group-sm flex-shrink-0"> <?php if ($ver): ?> <a href="<?= htmlspecialchars($ruta) ?>" target="_blank" class="btn btn-view" title="Ver"> <i class="fas fa-eye"></i> </a> <?php endif; ?> <a href="<?= htmlspecialchars($ruta) ?>" download class="btn btn-download" title="Descargar"> <i class="fas fa-download"></i> </a> <a href="index.php?controller=Anexo&action=eliminar&archivo=<?= urlencode($archivo) ?>" class="btn btn-delete btn-eliminar" title="Eliminar" data-nombre="<?= htmlspecialchars($archivo) ?>"> <i class="fas fa-trash-can"></i> </a> </div> </div> </div> </div> <?php endforeach; ?> <?php elseif ($dateFilter || $searchTerm): ?> <div class="col-12"> <div class="alert alert-warning"> <i class="fas fa-filter me-2"></i>No se encontraron documentos con los filtros aplicados. </div> </div> <?php else: ?> <div class="col-12"> <div class="alert alert-info"> <i class="fas fa-info-circle me-2"></i>No hay documentos en la carpeta de anexos. <a href="index.php?controller=Anexo&action=index" class="btn btn-danger btn-sm ms-2"> <i class="fas fa-upload me-1"></i>Subir ahora </a> </div> </div> <?php endif; ?> </div> <!-- Paginación --> <?php if ($totalPages > 1): $qBase = '&date=' . urlencode($dateFilter) . '&search=' . urlencode($searchTerm); ?> <div class="pagination-wrap"> <span class="pag-info"> Mostrando <?= $offset + 1 ?>–<?= min($offset + $itemsPerPage, $totalDocuments) ?> de <?= $totalDocuments ?> </span> <nav><ul class="pagination mb-0"> <?php if ($page > 1): ?> <li class="page-item"> <a class="page-link" href="index.php?controller=Anexo&action=visualizar&page=<?= $page-1 . $qBase ?>"> <i class="fas fa-chevron-left"></i> </a> </li> <?php endif; ?> <?php for ($i = max(1,$page-2); $i <= min($totalPages,$page+2); $i++): ?> <li class="page-item <?= $i==$page?'active':'' ?>"> <a class="page-link" href="index.php?controller=Anexo&action=visualizar&page=<?= $i . $qBase ?>"> <?= $i ?> </a> </li> <?php endfor; ?> <?php if ($page < $totalPages): ?> <li class="page-item"> <a class="page-link" href="index.php?controller=Anexo&action=visualizar&page=<?= $page+1 . $qBase ?>"> <i class="fas fa-chevron-right"></i> </a> </li> <?php endif; ?> </ul></nav> </div> <?php endif; ?> </div> </div> <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/sweetalert2/11.7.5/sweetalert2.all.min.js"></script> <script> // Confirmar eliminación document.querySelectorAll('.btn-eliminar').forEach(btn => { btn.addEventListener('click', function(e) { e.preventDefault(); const nombre = this.dataset.nombre; const href = this.href; Swal.fire({ title: '¿Eliminar archivo?', text: nombre, icon: 'warning', showCancelButton: true, confirmButtonColor: '#d0101d', cancelButtonText: 'Cancelar', confirmButtonText: 'Sí, eliminar' }).then(r => { if (r.isConfirmed) window.location = href; }); }); }); // Búsqueda en tiempo real const searchInput = document.getElementById('searchInput'); const clearBtn = document.getElementById('clearSearch'); const items = document.querySelectorAll('.document-item'); const noResults = document.getElementById('noResults'); const stats = document.getElementById('searchStats'); const count = document.getElementById('resultsCount'); const total = items.length; function selectDoc(card) { document.querySelectorAll('.document-card').forEach(c => c.classList.remove('selected')); card.classList.add('selected'); } function filterDocs() { const term = searchInput.value.toLowerCase().trim(); let visible = 0; clearBtn.style.display = term ? 'block' : 'none'; items.forEach(item => { const name = item.getAttribute('data-filename'); const show = !term || name.includes(term); item.style.display = show ? '' : 'none'; if (show) visible++; }); count.textContent = visible; noResults.style.display = (term && visible === 0) ? 'block' : 'none'; stats.style.display = (term && visible === 0) ? 'none' : 'block'; } function clearSearch() { searchInput.value = ''; clearBtn.style.display = 'none'; items.forEach(i => i.style.display = ''); count.textContent = total; noResults.style.display = 'none'; stats.style.display = 'block'; searchInput.focus(); } searchInput.addEventListener('input', () => { clearTimeout(window._st); window._st = setTimeout(filterDocs, 250); }); clearBtn.addEventListener('click', clearSearch); document.addEventListener('keydown', e => { if (e.key === 'Escape') clearSearch(); }); searchInput.focus(); window.selectDoc = selectDoc; </script> </body> </html>