IMPLEMENTACION DE LOS VIDEOS EN EL CATALOGO PUBLICO
This commit is contained in:
@@ -2687,6 +2687,42 @@ select {
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.video-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 14px;
|
||||
padding: 10px 14px;
|
||||
background: #fafaff;
|
||||
border: 1.5px solid #e8e6f0;
|
||||
border-radius: 10px;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.video-filename {
|
||||
flex: 1;
|
||||
color: #4a4a68;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.video-delete {
|
||||
background: none;
|
||||
border: none;
|
||||
color: #b91c1c;
|
||||
font-size: 1rem;
|
||||
cursor: pointer;
|
||||
padding: 4px 6px;
|
||||
border-radius: 6px;
|
||||
transition: background 0.2s;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.video-delete:hover {
|
||||
background: #fef2f2;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.vendedores-add {
|
||||
flex-direction: column;
|
||||
|
||||
@@ -186,6 +186,7 @@ async function cargarEstadisticas() {
|
||||
|
||||
document.getElementById('statProductos').textContent = productos.length;
|
||||
document.getElementById('statCategorias').textContent = categorias.length;
|
||||
cargarRedes(); // carga la sección de videos
|
||||
} catch (err) {
|
||||
console.error('Error cargando estadísticas:', err);
|
||||
}
|
||||
@@ -1858,12 +1859,15 @@ function setupProductosSearch() {
|
||||
// EXCEL/CSV — asignar nombre + precio por referencia
|
||||
// ════════════════════════════════
|
||||
function setupExcelUpload() {
|
||||
const input = document.getElementById('excelArchivo');
|
||||
const hint = document.getElementById('excelArchivoHint');
|
||||
const form = document.getElementById('formExcelUpload');
|
||||
const btn = document.getElementById('btnProcesarExcel');
|
||||
const input = document.getElementById('excelArchivo');
|
||||
const hint = document.getElementById('excelArchivoHint');
|
||||
const form = document.getElementById('formExcelUpload');
|
||||
const btn = document.getElementById('btnProcesarExcel');
|
||||
const selBtn = document.getElementById('btnSeleccionarExcel');
|
||||
if (!input || !form) return;
|
||||
|
||||
selBtn.addEventListener('click', () => input.click());
|
||||
|
||||
input.addEventListener('change', () => {
|
||||
const f = input.files && input.files[0];
|
||||
if (f) {
|
||||
@@ -2744,3 +2748,106 @@ function initGestores() {
|
||||
|
||||
// cargarDescargas() ahora usa los gestores
|
||||
// (definido arriba en la sección DESCARGAS)
|
||||
|
||||
// ════════════════════════════════
|
||||
// VIDEOS — gestión desde Dashboard
|
||||
// ════════════════════════════════
|
||||
async function cargarRedes() {
|
||||
await cargarListadoVideos();
|
||||
setupVideosForm();
|
||||
}
|
||||
|
||||
async function cargarListadoVideos() {
|
||||
const listEl = document.getElementById('videosList');
|
||||
const errEl = document.getElementById('videosError');
|
||||
errEl.classList.remove('show');
|
||||
try {
|
||||
const res = await fetch('/api/videos');
|
||||
const videos = await res.json();
|
||||
if (videos.length === 0) {
|
||||
listEl.innerHTML = '<p class="pedidos-empty" style="margin:0">No hay videos. Sube el primero.</p>';
|
||||
return;
|
||||
}
|
||||
listEl.innerHTML = videos.map(v => `
|
||||
<div class="video-item">
|
||||
<video src="${v.url}" controls preload="metadata" style="width:200px;border-radius:8px;max-height:120px;background:#000"></video>
|
||||
<span class="video-filename" title="${escapeHtml(v.filename)}">${escapeHtml(v.filename)}</span>
|
||||
<button type="button" class="video-delete" data-filename="${v.filename}" title="Eliminar">✕</button>
|
||||
</div>
|
||||
`).join('');
|
||||
|
||||
listEl.querySelectorAll('.video-delete').forEach(btn => {
|
||||
btn.addEventListener('click', async () => {
|
||||
if (!confirm('¿Eliminar este video?')) return;
|
||||
try {
|
||||
const res = await fetch(`/api/videos/${btn.dataset.filename}`, {
|
||||
method: 'DELETE',
|
||||
credentials: 'include'
|
||||
});
|
||||
if (!res.ok) {
|
||||
const data = await res.json().catch(() => ({}));
|
||||
throw new Error(data.error || 'Error al eliminar');
|
||||
}
|
||||
await cargarListadoVideos();
|
||||
} catch (err) {
|
||||
errEl.textContent = err.message;
|
||||
errEl.classList.add('show');
|
||||
}
|
||||
});
|
||||
});
|
||||
} catch (err) {
|
||||
listEl.innerHTML = `<p class="pedidos-empty" style="margin:0">Error: ${err.message}</p>`;
|
||||
}
|
||||
}
|
||||
|
||||
function setupVideosForm() {
|
||||
const btn = document.getElementById('btnSubirVideo');
|
||||
const input = document.getElementById('videoFileInput');
|
||||
const errEl = document.getElementById('videosError');
|
||||
const selBtn = document.getElementById('btnSeleccionarVideo');
|
||||
const nameEl = document.getElementById('videoFileName');
|
||||
if (!btn || btn.dataset.vidWired) return;
|
||||
btn.dataset.vidWired = '1';
|
||||
|
||||
selBtn.addEventListener('click', () => input.click());
|
||||
|
||||
input.addEventListener('change', () => {
|
||||
nameEl.textContent = input.files[0] ? input.files[0].name : 'Ningún archivo seleccionado';
|
||||
});
|
||||
|
||||
async function subir() {
|
||||
const file = input.files[0];
|
||||
if (!file) {
|
||||
errEl.textContent = 'Selecciona un archivo de video';
|
||||
errEl.classList.add('show');
|
||||
return;
|
||||
}
|
||||
errEl.classList.remove('show');
|
||||
btn.disabled = true;
|
||||
btn.textContent = '⏳ Subiendo…';
|
||||
try {
|
||||
const fd = new FormData();
|
||||
fd.append('video', file);
|
||||
const res = await fetch('/api/videos', {
|
||||
method: 'POST',
|
||||
credentials: 'include',
|
||||
body: fd
|
||||
});
|
||||
if (!res.ok) {
|
||||
const data = await res.json().catch(() => ({}));
|
||||
throw new Error(data.error || 'Error al subir');
|
||||
}
|
||||
input.value = '';
|
||||
nameEl.textContent = 'Ningún archivo seleccionado';
|
||||
await cargarListadoVideos();
|
||||
} catch (err) {
|
||||
errEl.textContent = err.message;
|
||||
errEl.classList.add('show');
|
||||
} finally {
|
||||
btn.disabled = false;
|
||||
btn.textContent = '+ Subir video';
|
||||
}
|
||||
}
|
||||
|
||||
btn.addEventListener('click', subir);
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=DM+Serif+Display:ital@0;1&family=Plus+Jakarta+Sans:wght@300;400;500;600;700;800&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="admin.css?v=22">
|
||||
<link rel="stylesheet" href="admin.css?v=24">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@@ -113,6 +113,25 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Videos -->
|
||||
<div class="view-content-card" style="margin-top:28px">
|
||||
<h2 class="view-section-title">🎬 Videos</h2>
|
||||
<p class="view-section-desc">Sube hasta 10 videos para que aparezcan en el catálogo público, debajo del botón "Ver catálogo".</p>
|
||||
|
||||
<div class="videos-form" style="display:flex;gap:10px;margin-bottom:16px;flex-wrap:wrap;align-items:center">
|
||||
<div class="form-group" style="flex:1;min-width:200px;margin-bottom:0">
|
||||
<input type="file" id="videoFileInput" accept="video/*" style="display:none">
|
||||
<div style="display:flex;gap:8px;align-items:center">
|
||||
<button type="button" class="btn btn-ghost" id="btnSeleccionarVideo" style="white-space:nowrap">📁 Seleccionar archivo</button>
|
||||
<span id="videoFileName" style="font-size:0.85rem;color:#8b8ba7;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">Ningún archivo seleccionado</span>
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" class="btn btn-primary" id="btnSubirVideo">+ Subir video</button>
|
||||
</div>
|
||||
<div class="form-error" id="videosError" role="alert"></div>
|
||||
<div class="videos-list" id="videosList" style="display:grid;gap:12px"></div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- PRODUCTOS VIEW -->
|
||||
@@ -206,8 +225,11 @@
|
||||
<form id="formExcelUpload" class="excel-form" autocomplete="off">
|
||||
<div class="form-group">
|
||||
<label for="excelArchivo">Archivo Excel/CSV</label>
|
||||
<input type="file" id="excelArchivo" accept=".xlsx,.xls,.csv" required>
|
||||
<span class="form-hint" id="excelArchivoHint">Ningún archivo seleccionado</span>
|
||||
<div style="display:flex;gap:8px;align-items:center">
|
||||
<input type="file" id="excelArchivo" accept=".xlsx,.xls,.csv" required style="display:none">
|
||||
<button type="button" class="btn btn-ghost" id="btnSeleccionarExcel">📁 Seleccionar archivo</button>
|
||||
<span class="form-hint" id="excelArchivoHint" style="margin:0">Ningún archivo seleccionado</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="excel-summary" id="excelSummary" style="display:none"></div>
|
||||
@@ -741,6 +763,6 @@
|
||||
</div>
|
||||
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.1/jszip.min.js"></script>
|
||||
<script src="admin.js?v=23"></script>
|
||||
<script src="admin.js?v=26"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -25,6 +25,23 @@ let cargando = true;
|
||||
// ════════════════════════════════
|
||||
// CARGA DE DATOS
|
||||
// ════════════════════════════════
|
||||
async function cargarRedes() {
|
||||
try {
|
||||
const res = await fetch('/api/videos');
|
||||
const videos = await res.json();
|
||||
const grid = document.getElementById('redesGrid');
|
||||
if (videos.length === 0) { grid.innerHTML = ''; return; }
|
||||
|
||||
grid.innerHTML = videos.map(v => `
|
||||
<div class="video-card">
|
||||
<video src="${v.url}" controls preload="metadata" playsinline></video>
|
||||
</div>
|
||||
`).join('');
|
||||
} catch (err) {
|
||||
console.error('Error cargando videos:', err);
|
||||
}
|
||||
}
|
||||
|
||||
async function cargarDatos() {
|
||||
try {
|
||||
const [resCats, resProds] = await Promise.all([
|
||||
@@ -608,6 +625,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
setupNav();
|
||||
setupCartModals();
|
||||
cargarDatos();
|
||||
cargarRedes();
|
||||
});
|
||||
|
||||
|
||||
|
||||
@@ -539,6 +539,64 @@ section {
|
||||
box-shadow: var(--shadow-sm);
|
||||
}
|
||||
|
||||
/* ════════════════════════════════
|
||||
REDES SOCIALES
|
||||
════════════════════════════════ */
|
||||
.redes-section {
|
||||
padding: 20px 20px 10px;
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.redes-grid {
|
||||
display: flex;
|
||||
gap: 24px;
|
||||
overflow-x: auto;
|
||||
scroll-snap-type: x mandatory;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
padding-bottom: 12px;
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: #d4d0e0 transparent;
|
||||
}
|
||||
|
||||
.redes-grid::-webkit-scrollbar {
|
||||
height: 6px;
|
||||
}
|
||||
|
||||
.redes-grid::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.redes-grid::-webkit-scrollbar-thumb {
|
||||
background: #d4d0e0;
|
||||
border-radius: 99px;
|
||||
}
|
||||
|
||||
.redes-grid .video-card {
|
||||
flex: 0 0 auto;
|
||||
width: 360px;
|
||||
max-width: 85vw;
|
||||
scroll-snap-align: start;
|
||||
border-radius: 12px;
|
||||
overflow: hidden;
|
||||
background: #000;
|
||||
box-shadow: 0 4px 20px rgba(0,0,0,0.12);
|
||||
}
|
||||
|
||||
.redes-grid .video-card video {
|
||||
width: 100%;
|
||||
display: block;
|
||||
max-height: 600px;
|
||||
object-fit: contain;
|
||||
background: #000;
|
||||
}
|
||||
|
||||
.redes-grid .red-embed .instagram-media {
|
||||
min-width: 326px !important;
|
||||
max-width: 100% !important;
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
/* ════════════════════════════════
|
||||
CONTADOR + PRODUCTOS
|
||||
════════════════════════════════ */
|
||||
|
||||
+7
-2
@@ -9,7 +9,7 @@
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=DM+Serif+Display:ital@0;1&family=Plus+Jakarta+Sans:wght@300;400;500;600;700;800&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="estilos.css?v=7">
|
||||
<link rel="stylesheet" href="estilos.css?v=13">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@@ -55,6 +55,11 @@
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- VIDEOS -->
|
||||
<section class="redes-section" id="redes">
|
||||
<div class="redes-grid" id="redesGrid"></div>
|
||||
</section>
|
||||
|
||||
<!-- PRODUCTOS -->
|
||||
<section class="productos-section" id="catalogo">
|
||||
<div class="section-header">
|
||||
@@ -184,6 +189,6 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="app.js?v=8"></script>
|
||||
<script src="app.js?v=13"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user