feat: visor PDF inline con iframe colapsable + botón descarga

This commit is contained in:
Ricardo Monla
2026-05-03 04:03:38 -03:00
parent 69be611d84
commit 38ab3b508a
4 changed files with 622 additions and 95 deletions
+73 -4
View File
@@ -1040,6 +1040,58 @@ body {
color: var(--color-primary);
font-weight: 600;
}
.pdf-viewer-wrap {
margin: 1rem 0;
border: 1px solid #d5e8f0;
border-radius: 8px;
overflow: hidden;
background: #f8fbfd;
}
.pdf-viewer-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0.7rem 1rem;
background: linear-gradient(135deg, #eaf2f8, #d4e6f1);
cursor: pointer;
user-select: none;
font-size: 0.9rem;
transition: background 0.2s;
}
.pdf-viewer-header:hover {
background: linear-gradient(135deg, #d4e6f1, #aed6f1);
}
.pdf-toggle {
transition: transform 0.3s;
font-size: 0.8rem;
opacity: 0.6;
}
.pdf-viewer-wrap.collapsed .pdf-toggle {
transform: rotate(-90deg);
}
.pdf-viewer {
width: 100%;
height: 70vh;
border: none;
display: block;
}
.pdf-viewer-wrap.collapsed .pdf-viewer,
.pdf-viewer-wrap.collapsed .pdf-download-link {
display: none;
}
.pdf-download-link {
display: block;
text-align: center;
padding: 0.5rem;
font-size: 0.82rem;
color: var(--color-primary);
text-decoration: none;
background: #eaf2f8;
border-top: 1px solid #d5e8f0;
}
.pdf-download-link:hover {
background: #d4e6f1;
}
.nota-vacio {
color: var(--color-text-muted);
font-style: italic;
@@ -1121,18 +1173,35 @@ body {
// Recurso descargable o enlace
if (tema.recursoURL) {
const icono = tema.recursoTexto === "Abrir Archivo" ? "📄" : "🔗";
const etiqueta = tema.recursoTexto === "Abrir Archivo" ? "Archivo" : "Enlace";
// Buscar archivo local descargado
let recursoHref = tema.recursoURL;
let recursoLocal = null;
if (tema.recursoID) {
const secDir = path.join(__dirname, "descargas", "secciones", modulo.carpeta, seccion.carpeta);
const archivos = fs.existsSync(secDir) ? fs.readdirSync(secDir).filter(f => f.startsWith(`recurso_${tema.recursoID}.`)) : [];
if (archivos.length > 0) {
recursoHref = `secciones/${modulo.carpeta}/${seccion.carpeta}/${archivos[0]}`;
recursoLocal = archivos[0];
recursoHref = `secciones/${modulo.carpeta}/${seccion.carpeta}/${recursoLocal}`;
}
}
bodyHTML += `<div class="nota-recurso">${icono} <strong>${etiqueta}:</strong> <a href="${recursoHref}" target="_blank">${tema.titulo}</a></div>\n`;
if (recursoLocal && recursoLocal.endsWith(".pdf")) {
// PDF inline con visor embebido
const pdfId = `pdf-${tema.id}-${tema.recursoID}`;
bodyHTML += `<div class="pdf-viewer-wrap">
<div class="pdf-viewer-header" onclick="this.parentElement.classList.toggle('collapsed')">
<span>📄 <strong>${tema.titulo}</strong></span>
<span class="pdf-toggle">▼</span>
</div>
<iframe class="pdf-viewer" src="${recursoHref}" id="${pdfId}"></iframe>
<a href="${recursoHref}" download class="pdf-download-link">⬇ Descargar PDF</a>
</div>\n`;
} else {
// Otros archivos (ZIP, BIN) o enlaces — link normal
const icono = recursoLocal ? "📦" : "🔗";
const etiqueta = recursoLocal ? "Descargar" : "Enlace";
bodyHTML += `<div class="nota-recurso">${icono} <strong>${etiqueta}:</strong> <a href="${recursoHref}" target="_blank">${tema.titulo}</a></div>\n`;
}
}
// Separador entre temas