{"id":5787,"date":"2026-01-14T17:19:03","date_gmt":"2026-01-14T17:19:03","guid":{"rendered":"https:\/\/utelvt.edu.ec\/biblioteca\/?page_id=5787"},"modified":"2026-01-26T05:29:49","modified_gmt":"2026-01-26T05:29:49","slug":"administracion-de-noticias","status":"publish","type":"page","link":"https:\/\/utelvt.edu.ec\/biblioteca\/administracion-de-noticias\/","title":{"rendered":"ADMINISTRACION DE NOTICIAS"},"content":{"rendered":"\n<!DOCTYPE html>\n<html lang=\"es\">\n<head>\n  <meta charset=\"UTF-8\">\n  <title>Administraci\u00f3n de P\u00e1gina de Noticias<\/title>\n  <style>\n\n\/* Contenedor del formulario *\/\n#section-admins form {\n  max-width: 500px;\n  background: #fff;\n  padding: 1.5rem;\n  border-radius: 8px;\n  box-shadow: 0 2px 8px rgba(0,0,0,0.1);\n  margin-bottom: 1.5rem;\n}\n\n#section-admins label {\n  display: block;\n  margin-top: 1rem;\n  font-weight: bold;\n  color: #008000;\n}\n\n#section-admins input[type=\"email\"] {\n  width: 100%;\n  padding: 0.6rem;\n  margin-top: 0.5rem;\n  border: 1px solid #ccc;\n  border-radius: 6px;\n}\n\n#add-admin-btn {\n  margin-top: 1rem;\n  padding: 0.7rem 1.5rem;\n  background: #008000;\n  color: #fff;\n  border: none;\n  border-radius: 6px;\n  cursor: pointer;\n  transition: background 0.3s ease;\n}\n\n#add-admin-btn:hover {\n  background: #006600;\n}\n\n\/* Lista de administradores *\/\n#admins-list {\n  background: #fff;\n  padding: 1rem;\n  border-radius: 8px;\n  box-shadow: 0 2px 8px rgba(0,0,0,0.1);\n}\n\n#admins-ul {\n  list-style: none;\n  padding: 0;\n  margin: 0;\n}\n\n#admins-ul li {\n  display: flex;\n  justify-content: space-between;\n  align-items: center;\n  background: #f9f9f9;\n  border: 1px solid #ddd;\n  border-radius: 6px;\n  padding: 0.6rem 0.8rem;\n  margin-bottom: 0.4rem;\n}\n\n.admin-actions {\n  display: flex;\n  gap: 0.5rem;\n}\n\n.admin-btn {\n  padding: 0.3rem 0.6rem;\n  border: none;\n  border-radius: 4px;\n  cursor: pointer;\n  font-size: 0.85rem;\n}\n\n.admin-btn.delete {\n  background: #a40000;\n  color: #fff;\n}\n\n.admin-btn.delete:hover {\n  background: #ff0000;\n}\n\n.admin-btn.edit {\n  background: #008000;\n  color: #fff;\n}\n\n.admin-btn.edit:hover {\n  background: #006600;\n}\n\n.resultado {\n  display: grid;\n  grid-template-columns: 2fr 1fr;\n  align-items: center;\n  gap: 10px;\n  padding: 8px;\n  border-bottom: 1px solid #ddd;\n}\n\n.resultado .titulo {\n  font-weight: bold;\n}\n\n.resultado .miniatura img {\n  width: 100px;\n  height: auto;\n  border-radius: 4px;\n}\n\n.resultado .fecha {\n  grid-column: 1 \/ span 2;\n  font-size: 0.85em;\n  color: #666;\n  margin-top: 4px;\n}\n\n.resultado-item {\n  display: flex;\n  align-items: center;\n  padding: 0.4rem;\n  border-bottom: 1px solid #ddd;\n}\n\n.resultado-item label {\n  margin-left: 0.5rem;\n  cursor: pointer;\n}\n\n#portadas-seleccionadas {\n  list-style: none;\n  padding: 0;\n  margin: 0;\n}\n\n.seleccionada-item {\n  display: flex;\n  justify-content: space-between;\n  align-items: center;\n  background: #f9f9f9;\n  border: 1px solid #ddd;\n  border-radius: 6px;\n  padding: 0.5rem 0.8rem;\n  margin-bottom: 0.4rem;\n  font-size: 15px;\n}\n\n.btn-eliminar {\n  color: #a40000;\n  cursor: pointer;\n  font-weight: bold;\n  padding: 0 6px;\n  transition: color 0.2s, transform 0.2s;\n}\n\n.btn-eliminar:hover {\n  color: #ff0000;\n  transform: scale(1.2);\n}\n\n\n\/* Estilo base de los botones *\/\n.modo-btn {\n  border: 2px solid #008000;   \/* verde UTELVT *\/\n  background-color: #008000;   \/* verde por defecto *\/\n  color: #fff;                 \/* texto blanco *\/\n  padding: 0.6rem 1.2rem;\n  border-radius: 6px;\n  cursor: pointer;\n  transition: all 0.3s ease;   \/* animaci\u00f3n suave *\/\n}\n\n\/* Hover: animaci\u00f3n *\/\n.modo-btn:hover {\n  background-color: #fff;      \/* se vuelve blanco *\/\n  color: #008000;              \/* texto verde *\/\n  box-shadow: 0 0 8px rgba(0,128,0,0.6); \/* efecto glow *\/\n}\n\n\/* Estado activo (cuando se selecciona) *\/\n.modo-btn.active {\n  background-color: #fff;      \/* fondo blanco *\/\n  color: #008000;              \/* texto verde *\/\n  border: 2px solid #008000;   \/* mantiene borde verde *\/\n}\n\n\/* Reset global *\/\n* { margin: 0; padding: 0; box-sizing: border-box; }\n\nhtml, body {\n  width: 100%;\n  height: 100%;\n  margin: 0;\n  padding: 0;\n  overflow-x: hidden;\n  font-family: 'Segoe UI', Arial, sans-serif;\n  background: #f9f9f9;\n}\n\n\/* Barra roja superior *\/\n.header-red {\n  position: relative;\n  left: 0;\n  right: 0;\n  width: 100%;\n  background-color: #c00000;\n  color: #fff;\n  text-align: center;\n  padding: 0.5rem;\n  font-size: 1rem;\n  font-weight: bold;\n}\n\n\/* Barra verde debajo *\/\n.header-green {\n  position: relative;\n  left: 0;\n  right: 0;\n  width: 100%;\n  background-color: #008000;\n  padding: 0.6rem 0;\n  display: flex;\n  justify-content: center;\n  gap: 1rem;\n}\n\n\/* Contenedor interno de los botones *\/\n.header-green .buttons-container {\n  max-width: 1200px;               \/* ancho m\u00e1ximo del bloque *\/\n  margin: 0 auto;                  \/* centra el bloque *\/\n  display: flex;\n  justify-content: center;\n  gap: 1rem;\n}\n\n\/* Botones de la barra verde *\/\n.header-green button {\n  background: none;\n  color: #fff;\n  border: none;\n  font-size: 1rem;\n  font-weight: bold;\n  cursor: pointer;\n  padding: 0.6rem 1rem;\n  position: relative;\n  z-index: 1;\n  transition: color 0.3s ease;\n}\n\n\/* Animaci\u00f3n hover *\/\n.header-green button::after {\n  content: \"\";\n  position: absolute;\n  left: 0; right: 0;\n  top: -0.6rem;\n  bottom: -0.6rem;\n  background: #f9f9f9;\n  z-index: -1;\n  transform: scaleY(0);\n  transform-origin: top;\n  transition: transform 0.3s ease;\n}\n\n.header-green button:hover { color: #008000; }\n.header-green button:hover::after { transform: scaleY(1); }\n\n\/* Estado activo *\/\n.header-green button.active {\n  color: #008000;\n}\n.header-green button.active::after {\n  background: #f9f9f9;   \/* igual que hover *\/\n  transform: scaleY(1);\n}\n\n\n\n    \/* Formulario y carrusel *\/\n    h1 { color: #008000; text-align: center; margin-top: 1rem; }\n    .main-container { display: flex; justify-content: center; align-items: flex-start; gap: 2rem; padding: 2rem; }\n    form { max-width: 600px; background: #fff; padding: 2rem; border-radius: 8px; box-shadow: 0 2px 8px rgba(0,0,0,0.1); transition: transform 0.6s ease; }\n    form.slide-left { transform: translateX(-150px); }\n    label { display: block; margin-top: 1rem; font-weight: bold; }\n    input[type=\"text\"], textarea { width: 100%; padding: 0.5rem; margin-top: 0.5rem; border: 1px solid #ccc; border-radius: 4px; }\n    button, .btn-wp { margin-top: 1rem; padding: 0.7rem 1.5rem; background: #008000; color: #fff; border: none; border-radius: 4px; cursor: pointer; text-decoration: none; display: inline-block; }\n    button:hover, .btn-wp:hover { background: #006600; }\n    .image-field { margin-top: 0.5rem; }\n    .error-msg { margin-top: 0.25rem; font-size: 0.9rem; color: red; min-height: 1em; }\n    #contador { margin-top: 1rem; font-weight: bold; color: #333; }\n    #mensaje-error { margin-top: 1rem; padding: 0.8rem; background: #ffeded; color: #a40000; border: 1px solid #a40000; border-radius: 6px; display: none; }\n\n    \/* Carrusel *\/\n    .preview-container { max-width: 400px; background: #fff; padding: 1rem; border-radius: 8px; box-shadow: 0 2px 8px rgba(0,0,0,0.1); opacity: 0; transform: translateX(50px); transition: opacity 0.6s ease, transform 0.6s ease; display: none; }\n    .preview-container.visible { display: block; opacity: 1; transform: translateX(0); }\n    .carousel { position: relative; width: 100%; aspect-ratio: 4\/5; overflow: hidden; background-color: #000; }\n    .carousel img { width: 100%; height: 100%; object-fit: contain; }\n\n    \/* Bot\u00f3n eliminar *\/\n    .delete-btn { position: absolute; top: 2px; right: 10px; background: none; border: none; cursor: pointer; padding: 0; }\n    .delete-btn img { width: 28px; height: 28px; transition: transform 0.3s ease; }\n    .delete-btn:hover img { transform: rotate(90deg); }\n\n    \/* Flechas dentro del carrusel *\/\n    .arrow { position: absolute; top: 50%; transform: translateY(-50%); background: rgba(0,0,0,0.5); border-radius: 50%; padding: 6px; cursor: pointer; transition: transform 0.3s ease, background 0.3s ease; display: none; }\n    .arrow img { width: 30px; height: 30px; }\n    .arrow:hover { transform: translateY(-50%) scale(1.1); background: rgba(0,0,0,0.7); }\n    .arrow-left { left: 10px; }\n    .arrow-right { right: 10px; }\n\n    \/* Miniaturas *\/\n    .thumbnails { display: flex; flex-wrap: wrap; justify-content: center; gap: 0.5rem; margin-top: 0.5rem; }\n    .thumbnails img { width: 60px; height: 60px; object-fit: cover; border: 2px solid transparent; cursor: pointer; border-radius: 4px; }\n    .thumbnails img.active { border-color: #008000; }\n\n    \/* Secciones *\/\n    .section { display: none; padding: 2rem; }\n    .section.active { display: block; }\n\/* Bot\u00f3n activo: mismo efecto que hover *\/\n.header-green button.active {\n  color: #008000;\n}\n\n.header-green button.active::after {\n  transform: scaleY(1);\n}\n\n  <\/style>\n<\/head>\n<body>\n\n  <div class=\"header-red\">\n  ADMINISTRACION DE PAGINA DE NOTICIAS\n<\/div>\n\n<div class=\"header-green\">\n  <div class=\"buttons-container\">\n    <button id=\"btn-noticias\">Regresar a la p\u00e1gina de noticias<\/button>\n    <button id=\"btn-publicaciones\">Subir publicaciones<\/button>\n    <button id=\"btn-eventos\">Subir eventos<\/button>\n    <button id=\"btn-portada\">Editar portada de noticias<\/button>\n    <button id=\"btn-admins\">Administradores<\/button>\n  <\/div>\n<\/div>\n\n\n\n  <div id=\"panel-admin-noticias\" style=\"display:none;\">\n    <!-- Aqu\u00ed va tu l\u00f3gica de administraci\u00f3n (checkboxes, guardar portadas, etc.) -->\n  <\/div>\n\n  <!-- Secci\u00f3n Publicaciones -->\n  <div id=\"section-publicaciones\" class=\"section\">\n    <h1>Aqu\u00ed puedes subir tus publicaciones a la p\u00e1gina de noticias<\/h1>\n    <div class=\"main-container\">\n\n      <!-- Formulario -->\n      <form id=\"admin-form\" autocomplete=\"off\">\n        <label for=\"titulo\">T\u00edtulo<\/label>\n        <input type=\"text\" id=\"titulo\" name=\"titulo\" required autocomplete=\"off\">\n\n        <label for=\"descripcion\">Descripci\u00f3n<\/label>\n        <textarea id=\"descripcion\" name=\"descripcion\" rows=\"4\" required autocomplete=\"off\"><\/textarea>\n\n        <!-- Im\u00e1genes -->\n        <label>Im\u00e1genes (m\u00e1ximo 10)<\/label>\n        <div id=\"imagenes-container\">\n          <div class=\"image-field\">\n            <input type=\"text\" name=\"imagen_url[]\" placeholder=\"Pega aqu\u00ed la URL de la imagen\" autocomplete=\"off\">\n            <div class=\"error-msg\"><\/div>\n          <\/div>\n        <\/div>\n\n        <a href=\"https:\/\/utelvt.edu.ec\/biblioteca\/wp-admin\/upload.php\" target=\"_blank\" class=\"btn-wp\">\n          Subir\/Seleccionar imagen en WordPress\n        <\/a>\n\n        <div id=\"contador\">Im\u00e1genes seleccionadas: 0<\/div>\n        <div id=\"mensaje-error\"><\/div>\n\n        <button id=\"submit-btn\" type=\"submit\">Guardar en Base de Datos<\/button>\n      <\/form>\n\n      <!-- Carrusel -->\n      <div class=\"preview-container\" id=\"preview-container\">\n        <h2>Im\u00e1genes seleccionadas<\/h2>\n        <div class=\"carousel\" id=\"carousel\"><\/div>\n        <div class=\"thumbnails\" id=\"thumbnails\"><\/div>\n      <\/div>\n\n    <\/div>\n  <\/div>\n\n  <!-- Secci\u00f3n Eventos -->\n  <div id=\"section-eventos\" class=\"section\">\n    <h1>Aqu\u00ed puedes subir tus eventos a la p\u00e1gina de noticias<\/h1>\n    <div class=\"main-container\">\n\n      <!-- Formulario -->\n      <form id=\"form-eventos\" autocomplete=\"off\">\n        <label for=\"evento-titulo\">T\u00edtulo<\/label>\n        <input type=\"text\" id=\"evento-titulo\" name=\"titulo\" required autocomplete=\"off\">\n\n        <label for=\"evento-descripcion\">Descripci\u00f3n<\/label>\n        <textarea id=\"evento-descripcion\" name=\"descripcion\" rows=\"4\" required autocomplete=\"off\"><\/textarea>\n\n        <!-- Fecha y hora del evento -->\n        <label for=\"evento-fecha\">Fecha del evento<\/label>\n        <input type=\"date\" id=\"evento-fecha\" name=\"fecha\" required>\n\n        <label for=\"evento-hora\">Hora del evento<\/label>\n        <input type=\"time\" id=\"evento-hora\" name=\"hora\" required>\n\n        <!-- Im\u00e1genes -->\n        <label>Im\u00e1genes (m\u00e1ximo 10, opcional)<\/label>\n        <div id=\"imagenes-container-eventos\">\n          <div class=\"image-field\">\n            <input type=\"text\" name=\"imagen_url_eventos[]\" placeholder=\"Pega aqu\u00ed la URL de la imagen\" autocomplete=\"off\">\n            <div class=\"error-msg\"><\/div>\n          <\/div>\n        <\/div>\n\n        <a href=\"https:\/\/utelvt.edu.ec\/biblioteca\/wp-admin\/upload.php\" target=\"_blank\" class=\"btn-wp\">\n          Subir\/Seleccionar imagen en WordPress\n        <\/a>\n\n        <div id=\"contador-eventos\">Im\u00e1genes seleccionadas: 0<\/div>\n        <div id=\"mensaje-error-eventos\"><\/div>\n\n        <button id=\"evento-submit\" type=\"submit\">Guardar evento<\/button>\n      <\/form>\n\n      <!-- Carrusel -->\n      <div class=\"preview-container\" id=\"preview-container-eventos\">\n        <h2>Im\u00e1genes seleccionadas<\/h2>\n        <div class=\"carousel\" id=\"carousel-eventos\"><\/div>\n        <div class=\"thumbnails\" id=\"thumbnails-eventos\"><\/div>\n      <\/div>\n\n    <\/div>\n  <\/div>\n\n  <!-- Secci\u00f3n Portada -->\n  <div id=\"section-portada\" class=\"section\">\n    <h1>Aqui puedes editar las 3 primeras portadas o dejarlas por default del sistema<\/h1>\n\n    <div class=\"modo-selector\">\n      <button id=\"modo-auto\" class=\"modo-btn\">Modo autom\u00e1tico<\/button>\n      <button id=\"modo-organizar\" class=\"modo-btn\">Organizar portadas<\/button>\n    <\/div>\n\n    <div id=\"organizar-portadas\" style=\"display:none; margin-top:1rem;\">\n      <label for=\"buscador-portadas\">Buscar noticia<\/label>\n      <input type=\"text\" id=\"buscador-portadas\" placeholder=\"Escriba t\u00edtulo o palabra clave\" autocomplete=\"off\">\n      <button id=\"btn-buscar-portadas\">Buscar<\/button>\n\n      <div id=\"resultados-portadas\" style=\"margin-top:1rem;\"><\/div>\n\n      <h3 style=\"margin-top:2rem;\">Portadas seleccionadas<\/h3>\n      <ol id=\"portadas-seleccionadas\"><\/ol>\n\n      <button id=\"guardar-portadas\" style=\"margin-top:1rem;\">Guardar portadas prioritarias<\/button>\n    <\/div>\n\n    <div id=\"mensaje-portadas\" style=\"margin-top:1rem; color:#008000; font-weight:bold;\"><\/div>\n  <\/div>\n\n  <!-- Secci\u00f3n Administradores -->\n  <div id=\"section-admins\" class=\"section\">\n    <h1>Gesti\u00f3n de Administradores<\/h1>\n    <div class=\"main-container\">\n      <form id=\"admins-form\" autocomplete=\"off\">\n        <label for=\"admin-email\">Correo del nuevo administrador<\/label>\n        <input type=\"email\" id=\"admin-email\" name=\"admin-email\" required autocomplete=\"off\">\n        <button id=\"add-admin-btn\" type=\"submit\">A\u00f1adir Administrador<\/button>\n      <\/form>\n      <div id=\"admins-list\">\n        <h2>Lista de administradores<\/h2>\n        <ul id=\"admins-ul\"><\/ul>\n      <\/div>\n    <\/div>\n  <\/div>\n\n<\/body>\n\n<\/html>\n\n<!-- Importar la librer\u00eda de Supabase -->\n<script src=\"https:\/\/cdn.jsdelivr.net\/npm\/@supabase\/supabase-js@2\"><\/script>\n\n<script>\n  \/\/ Inicializar Supabase\n  const supabaseUrl = \"https:\/\/mokvdfuarrxnjmuzvsfj.supabase.co\";\n  const supabaseKey = \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Im1va3ZkZnVhcnJ4bmptdXp2c2ZqIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NjgzMTkzNzYsImV4cCI6MjA4Mzg5NTM3Nn0.TQbSQNDgzwAxr2tAUQyBtIOVKuqN1d29to4BrdRjI-s\";\n  const sb = supabase.createClient(supabaseUrl, supabaseKey);\n\n  \/\/ Botones y secciones\n  const btnNoticias = document.getElementById(\"btn-noticias\");\n  const btnPublicaciones = document.getElementById(\"btn-publicaciones\");\n  const btnEventos = document.getElementById(\"btn-eventos\");\n  const btnPortada = document.getElementById(\"btn-portada\");\n\n  const sectionPublicaciones = document.getElementById(\"section-publicaciones\");\n  const sectionEventos = document.getElementById(\"section-eventos\");\n  const sectionPortada = document.getElementById(\"section-portada\");\n\n function activarSeccion(btn, section) {\n  \/\/ Quitar active de todos los botones\n  [btnNoticias, btnPublicaciones, btnEventos, btnPortada, btnAdmins].forEach(b => b.classList.remove(\"active\"));\n\n  \/\/ Ocultar todas las secciones\n  [sectionPublicaciones, sectionEventos, sectionPortada, sectionAdmins].forEach(s => {\n    if (s) s.classList.remove(\"active\");\n  });\n\n  \/\/ Marcar bot\u00f3n y mostrar secci\u00f3n\n  btn.classList.add(\"active\");\n  section.classList.add(\"active\");\n}\n\nbtnNoticias.addEventListener(\"click\", () => {\n  window.location.href = \"https:\/\/utelvt.edu.ec\/biblioteca\/noticias-biblioteca\/\";\n});\nbtnPublicaciones.addEventListener(\"click\", () => activarSeccion(btnPublicaciones, sectionPublicaciones));\nbtnEventos.addEventListener(\"click\", () => activarSeccion(btnEventos, sectionEventos));\nbtnPortada.addEventListener(\"click\", () => activarSeccion(btnPortada, sectionPortada));\nbtnAdmins.addEventListener(\"click\", () => {\n  activarSeccion(btnAdmins, sectionAdmins);\n  cargarAdmins();\n});\n\n\/\/ Variables formulario publicaciones\nconst form = document.getElementById(\"admin-form\");\nconst imagenesContainer = document.getElementById(\"imagenes-container\");\nconst carousel = document.getElementById(\"carousel\");\nconst thumbnails = document.getElementById(\"thumbnails\");\nconst previewContainer = document.getElementById(\"preview-container\");\nconst contador = document.getElementById(\"contador\");\nconst mensajeError = document.getElementById(\"mensaje-error\");\nconst dominioPermitido = \"https:\/\/utelvt.edu.ec\/biblioteca\/wp-content\/uploads\/\";\nconst maxImagenes = 10;\nlet imagenes = [];\nlet currentIndex = 0;\nlet id = null;\n\n\/\/ Detectar si hay id en la URL\nconst params = new URLSearchParams(window.location.search);\nid = params.get(\"id\");\n\n\/\/ Cambiar texto del bot\u00f3n seg\u00fan si es edici\u00f3n o inserci\u00f3n\nconst submitBtn = document.querySelector(\"#submit-btn\");\nif (id) {\n  submitBtn.textContent = \"Actualizar publicaci\u00f3n\";\n} else {\n  submitBtn.textContent = \"Guardar en Base de Datos\";\n}\n\n  function resetFormulario() {\n    form.reset();\n    imagenesContainer.innerHTML = `\n      <div class=\"image-field\">\n        <input type=\"text\" name=\"imagen_url[]\" placeholder=\"Pega aqu\u00ed la URL de la imagen\" autocomplete=\"off\">\n        <div class=\"error-msg\"><\/div>\n      <\/div>\n    `;\n    carousel.innerHTML = `\n      <div class=\"arrow arrow-left\" id=\"prevBtn\">\n        <img decoding=\"async\" src=\"https:\/\/utelvt.edu.ec\/biblioteca\/wp-content\/uploads\/2026\/01\/FLECHA-TARJETAS-IZQUIERDA-BLANCA.png\" alt=\"Anterior\">\n      <\/div>\n      <div class=\"arrow arrow-right\" id=\"nextBtn\">\n        <img decoding=\"async\" src=\"https:\/\/utelvt.edu.ec\/biblioteca\/wp-content\/uploads\/2026\/01\/FLECHA-TARJETAS-DERECHA-BLANCA.png\" alt=\"Siguiente\">\n      <\/div>\n    `;\n    thumbnails.innerHTML = \"\";\n    contador.textContent = \"Im\u00e1genes seleccionadas: 0\";\n    mensajeError.style.display = \"none\";\n    mensajeError.textContent = \"\";\n    form.classList.remove(\"slide-left\");\n    previewContainer.classList.remove(\"visible\");\n    previewContainer.style.display = \"none\";\n    imagenes = [];\n    currentIndex = 0;\n    addArrowEvents();\n  }\n\n  function mostrarImagen(index) {\n    if (imagenes.length > 0) {\n      carousel.innerHTML = `\n        <img decoding=\"async\" src=\"${imagenes[index]}\" alt=\"Vista previa\">\n        <button class=\"delete-btn\" onclick=\"eliminarImagen(${index})\">\n          <img decoding=\"async\" src=\"https:\/\/utelvt.edu.ec\/biblioteca\/wp-content\/uploads\/2026\/01\/ILIMPNAR-PNG.png\" alt=\"Eliminar\">\n        <\/button>\n        <div class=\"arrow arrow-left\" id=\"prevBtn\">\n          <img decoding=\"async\" src=\"https:\/\/utelvt.edu.ec\/biblioteca\/wp-content\/uploads\/2026\/01\/FLECHA-TARJETAS-IZQUIERDA-BLANCA.png\" alt=\"Anterior\">\n        <\/div>\n        <div class=\"arrow arrow-right\" id=\"nextBtn\">\n          <img decoding=\"async\" src=\"https:\/\/utelvt.edu.ec\/biblioteca\/wp-content\/uploads\/2026\/01\/FLECHA-TARJETAS-DERECHA-BLANCA.png\" alt=\"Siguiente\">\n        <\/div>\n      `;\n      actualizarMiniaturas();\n      addArrowEvents();\n      toggleArrows();\n    }\n  }\n\n  function actualizarMiniaturas() {\n    thumbnails.innerHTML = \"\";\n    imagenes.forEach((url, i) => {\n      const thumb = document.createElement(\"img\");\n      thumb.src = url;\n      if (i === currentIndex) thumb.classList.add(\"active\");\n      thumb.addEventListener(\"click\", () => {\n        currentIndex = i;\n        mostrarImagen(currentIndex);\n      });\n      thumbnails.appendChild(thumb);\n    });\n  }\n\n  function eliminarImagen(index) {\n    const urlAEliminar = imagenes[index];\n    imagenes.splice(index, 1);\n    const inputs = imagenesContainer.querySelectorAll(\"input[name='imagen_url[]']\");\n    inputs.forEach(input => {\n      if (input.value.trim() === urlAEliminar) {\n        input.parentElement.remove();\n      }\n    });\n    if (currentIndex >= imagenes.length) {\n      currentIndex = imagenes.length - 1;\n    }\n    actualizarVista();\n    if (imagenes.length > 0) {\n      mostrarImagen(currentIndex);\n    }\n  }\n\n  function actualizarVista() {\n    const inputs = imagenesContainer.querySelectorAll(\"input[name='imagen_url[]']\");\n    imagenes = [];\n    let totalValidas = 0;\n    inputs.forEach(input => {\n      const url = input.value.trim();\n      const errorMsg = input.nextElementSibling;\n      if (url !== \"\" && url.startsWith(dominioPermitido)) {\n        totalValidas++;\n        imagenes.push(url);\n        errorMsg.textContent = \"\";\n      } else if (url !== \"\" && !url.startsWith(dominioPermitido)) {\n        errorMsg.textContent = \"&#x274c; Seleccione links correctos de WordPress\";\n      } else {\n        errorMsg.textContent = \"\";\n      }\n    });\n    contador.textContent = \"Im\u00e1genes seleccionadas: \" + totalValidas;\n\n    if (totalValidas > 0) {\n      form.classList.add(\"slide-left\");\n      previewContainer.style.display = \"block\";\n      setTimeout(() => previewContainer.classList.add(\"visible\"), 50);\n      if (currentIndex < 0) currentIndex = 0;\n      if (currentIndex >= imagenes.length) currentIndex = imagenes.length - 1;\n      mostrarImagen(currentIndex);\n    } else {\n      form.classList.remove(\"slide-left\");\n      previewContainer.classList.remove(\"visible\");\n      previewContainer.style.display = \"none\";\n    }\n  }\n\n  function toggleArrows() {\n    const prevBtn = document.getElementById(\"prevBtn\");\n    const nextBtn = document.getElementById(\"nextBtn\");\n    if (imagenes.length >= 2) {\n      if (prevBtn) prevBtn.style.display = \"block\";\n      if (nextBtn) nextBtn.style.display = \"block\";\n    } else {\n      if (prevBtn) prevBtn.style.display = \"none\";\n      if (nextBtn) nextBtn.style.display = \"none\";\n    }\n  }\n\n  function addArrowEvents() {\n    const prevBtn = document.getElementById(\"prevBtn\");\n    const nextBtn = document.getElementById(\"nextBtn\");\n    if (prevBtn) {\n      prevBtn.addEventListener(\"click\", () => {\n        if (imagenes.length > 0) {\n          currentIndex = (currentIndex - 1 + imagenes.length) % imagenes.length;\n          mostrarImagen(currentIndex);\n        }\n      });\n    }\n    if (nextBtn) {\n      nextBtn.addEventListener(\"click\", () => {\n        if (imagenes.length > 0) {\n          currentIndex = (currentIndex + 1) % imagenes.length;\n          mostrarImagen(currentIndex);\n        }\n      });\n    }\n  }\n\n  \/\/ Campos din\u00e1micos y validaci\u00f3n de URLs\n  imagenesContainer.addEventListener(\"input\", (e) => {\n    if (e.target.name === \"imagen_url[]\") {\n      const url = e.target.value.trim();\n      const errorMsg = e.target.nextElementSibling;\n      if (url !== \"\" && !url.startsWith(dominioPermitido)) {\n        errorMsg.textContent = \"&#x274c; Seleccione links correctos de WordPress\";\n      } else {\n        errorMsg.textContent = \"\";\n      }\n\n      const inputs = imagenesContainer.querySelectorAll(\"input[name='imagen_url[]']\");\n      const ultimo = inputs[inputs.length - 1];\n      if (ultimo.value.trim() !== \"\" && ultimo.value.startsWith(dominioPermitido) && inputs.length < maxImagenes) {\n        const nuevoCampo = document.createElement(\"div\");\n        nuevoCampo.classList.add(\"image-field\");\n        nuevoCampo.innerHTML = `\n          <input type=\"text\" name=\"imagen_url[]\" placeholder=\"Pega aqu\u00ed la URL de la imagen\" autocomplete=\"off\">\n          <div class=\"error-msg\"><\/div>\n        `;\n        imagenesContainer.appendChild(nuevoCampo);\n      }\n    }\n    actualizarVista();\n  });\n\n  imagenesContainer.addEventListener(\"blur\", (e) => {\n    if (e.target.name === \"imagen_url[]\" && e.target.value.trim() === \"\") {\n      const inputs = imagenesContainer.querySelectorAll(\"input[name='imagen_url[]']\");\n      if (inputs.length > 1 && e.target === inputs[inputs.length - 2]) {\n        e.target.parentElement.remove();\n      }\n      actualizarVista();\n    }\n  }, true);\n\n  \/\/ Guardar o actualizar\n  form.addEventListener(\"submit\", async (e) => {\n    e.preventDefault();\n    mensajeError.style.display = \"none\";\n    mensajeError.textContent = \"\";\n\n    const titulo = document.getElementById(\"titulo\").value.trim();\n    const descripcion = document.getElementById(\"descripcion\").value.trim();\n    const imagenesValidas = imagenes.filter(url => url.startsWith(dominioPermitido));\n    const total = imagenesValidas.length;\n\n    if (titulo === \"\" || descripcion === \"\" || total === 0) {\n      mensajeError.style.display = \"block\";\n      mensajeError.innerHTML = \"&#x274c; Por favor llene todos los campos obligatorios (T\u00edtulo, Descripci\u00f3n y al menos una imagen v\u00e1lida).\";\n      return;\n    }\n\n    let data, error;\n    if (id) {\n      const idNum = parseInt(id, 10);\n      ({ data, error } = await sb\n        .from(\"imagenes_portada\")\n        .update({ titulo, descripcion, imagenes: imagenesValidas, total })\n        .eq(\"id\", idNum));\n    } else {\n      ({ data, error } = await sb\n        .from(\"imagenes_portada\")\n        .insert([{ titulo, descripcion, imagenes: imagenesValidas, total }]));\n    }\n\n    if (error) {\n      mensajeError.style.display = \"block\";\n      mensajeError.innerHTML = \"&#x274c; Error al guardar: \" + error.message;\n    } else {\n      alert(\"&#x2705; Publicaci\u00f3n \" + (id ? \"actualizada\" : \"guardada\") + \" correctamente\");\n      if (id) {\n        window.location.href = \"https:\/\/utelvt.edu.ec\/biblioteca\/noticias-biblioteca\/\";\n      } else {\n        resetFormulario();\n      }\n    }\n  });\n\n  window.addEventListener(\"load\", resetFormulario);\n  window.addEventListener(\"pageshow\", (event) => {\n    if (event.persisted) resetFormulario();\n  });\n\n  actualizarVista();\n\n  \/\/ Cargar datos si venimos con ?id= en la URL\n  async function cargarEdicion() {\n    if (!id) return;\n\n    const idNum = parseInt(id, 10);\n    const { data, error } = await sb\n      .from(\"imagenes_portada\")\n      .select(\"*\")\n      .eq(\"id\", idNum)\n      .single();\n\n    if (error) {\n      console.error(\"Error al cargar publicaci\u00f3n:\", error.message);\n      return;\n    }\n    if (!data) {\n      console.warn(\"No se encontr\u00f3 publicaci\u00f3n con id:\", idNum);\n      return;\n    }\n\n    document.getElementById(\"titulo\").value = data.titulo || \"\";\n    document.getElementById(\"descripcion\").value = data.descripcion || \"\";\n\n    imagenesContainer.innerHTML = \"\";\n    imagenes = data.imagenes || [];\n    imagenes.forEach(url => {\n      const campo = document.createElement(\"div\");\n      campo.classList.add(\"image-field\");\n      campo.innerHTML = `\n        <input type=\"text\" name=\"imagen_url[]\" value=\"${url}\" autocomplete=\"off\">\n        <div class=\"error-msg\"><\/div>\n      `;\n      imagenesContainer.appendChild(campo);\n    });\n\n    if (imagenes.length < maxImagenes) {\n      const nuevoCampo = document.createElement(\"div\");\n      nuevoCampo.classList.add(\"image-field\");\n      nuevoCampo.innerHTML = `\n        <input type=\"text\" name=\"imagen_url[]\" placeholder=\"Pega aqu\u00ed la URL de la imagen\" autocomplete=\"off\">\n        <div class=\"error-msg\"><\/div>\n      `;\n      imagenesContainer.appendChild(nuevoCampo);\n    }\n\n    contador.textContent = \"Im\u00e1genes seleccionadas: \" + imagenes.length;\n    currentIndex = 0;\n    actualizarVista();\n  }\n\n  document.addEventListener(\"DOMContentLoaded\", cargarEdicion);\n<\/script>\n\n\n<script>\n\/\/ Variables formulario eventos\nconst formEventos = document.getElementById(\"form-eventos\");\nconst imagenesContainerEventos = document.getElementById(\"imagenes-container-eventos\");\nconst carouselEventos = document.getElementById(\"carousel-eventos\");\nconst thumbnailsEventos = document.getElementById(\"thumbnails-eventos\");\nconst previewContainerEventos = document.getElementById(\"preview-container-eventos\");\nconst contadorEventos = document.getElementById(\"contador-eventos\");\nconst mensajeErrorEventos = document.getElementById(\"mensaje-error-eventos\");\nconst dominioPermitidoEventos = \"https:\/\/utelvt.edu.ec\/biblioteca\/wp-content\/uploads\/\";\nconst maxImagenesEventos = 10;\n\nlet imagenesEventos = [];\nlet currentIndexEventos = 0;\nlet idEvento = null;\n\n\/\/ Detectar si hay id en la URL\nconst paramsEventos = new URLSearchParams(window.location.search);\nidEvento = paramsEventos.get(\"id\");\n\n\/\/ Cambiar texto del bot\u00f3n seg\u00fan si es edici\u00f3n o inserci\u00f3n\nconst submitBtnEventos = document.querySelector(\"#evento-submit\");\nsubmitBtnEventos.textContent = idEvento ? \"Actualizar evento\" : \"Guardar evento\";\n\n  function resetFormularioEventos() {\n  formEventos.reset();\n  imagenesContainerEventos.innerHTML = `\n    <div class=\"image-field\">\n      <input type=\"text\" name=\"imagen_url_eventos[]\" placeholder=\"Pega aqu\u00ed la URL de la imagen\" autocomplete=\"off\">\n      <div class=\"error-msg\"><\/div>\n    <\/div>\n  `;\n  carouselEventos.innerHTML = `\n    <div class=\"arrow arrow-left\" id=\"prevBtnEventos\">\n      <img decoding=\"async\" src=\"https:\/\/utelvt.edu.ec\/biblioteca\/wp-content\/uploads\/2026\/01\/FLECHA-TARJETAS-IZQUIERDA-BLANCA.png\" alt=\"Anterior\">\n    <\/div>\n    <div class=\"arrow arrow-right\" id=\"nextBtnEventos\">\n      <img decoding=\"async\" src=\"https:\/\/utelvt.edu.ec\/biblioteca\/wp-content\/uploads\/2026\/01\/FLECHA-TARJETAS-DERECHA-BLANCA.png\" alt=\"Siguiente\">\n    <\/div>\n  `;\n  thumbnailsEventos.innerHTML = \"\";\n  contadorEventos.textContent = \"Im\u00e1genes seleccionadas: 0\";\n  mensajeErrorEventos.style.display = \"none\";\n  mensajeErrorEventos.textContent = \"\";\n  formEventos.classList.remove(\"slide-left\");\n  previewContainerEventos.classList.remove(\"visible\");\n  previewContainerEventos.style.display = \"none\";\n  imagenesEventos = [];\n  currentIndexEventos = 0;\n  addArrowEventsEventos();\n  toggleArrowsEventos();\n}\n\n\n  function mostrarImagenEventos(index) {\n  if (imagenesEventos.length > 0) {\n    carouselEventos.innerHTML = `\n      <img decoding=\"async\" src=\"${imagenesEventos[index]}\" alt=\"Vista previa\">\n      <button class=\"delete-btn\" onclick=\"eliminarImagenEventos(${index})\">\n        <img decoding=\"async\" src=\"https:\/\/utelvt.edu.ec\/biblioteca\/wp-content\/uploads\/2026\/01\/ILIMPNAR-PNG.png\" alt=\"Eliminar\">\n      <\/button>\n      <div class=\"arrow arrow-left\" id=\"prevBtnEventos\">\n        <img decoding=\"async\" src=\"https:\/\/utelvt.edu.ec\/biblioteca\/wp-content\/uploads\/2026\/01\/FLECHA-TARJETAS-IZQUIERDA-BLANCA.png\" alt=\"Anterior\">\n      <\/div>\n      <div class=\"arrow arrow-right\" id=\"nextBtnEventos\">\n        <img decoding=\"async\" src=\"https:\/\/utelvt.edu.ec\/biblioteca\/wp-content\/uploads\/2026\/01\/FLECHA-TARJETAS-DERECHA-BLANCA.png\" alt=\"Siguiente\">\n      <\/div>\n    `;\n    actualizarMiniaturasEventos();\n    addArrowEventsEventos();\n    toggleArrowsEventos();\n  }\n}\n\nfunction actualizarMiniaturasEventos() {\n  thumbnailsEventos.innerHTML = \"\";\n  imagenesEventos.forEach((url, i) => {\n    const thumb = document.createElement(\"img\");\n    thumb.src = url;\n    if (i === currentIndexEventos) thumb.classList.add(\"active\");\n    thumb.addEventListener(\"click\", () => {\n      currentIndexEventos = i;\n      mostrarImagenEventos(currentIndexEventos);\n    });\n    thumbnailsEventos.appendChild(thumb);\n  });\n}\n\nfunction toggleArrowsEventos() {\n  const prevBtn = document.getElementById(\"prevBtnEventos\");\n  const nextBtn = document.getElementById(\"nextBtnEventos\");\n  if (imagenesEventos.length >= 2) {\n    if (prevBtn) prevBtn.style.display = \"block\";\n    if (nextBtn) nextBtn.style.display = \"block\";\n  } else {\n    if (prevBtn) prevBtn.style.display = \"none\";\n    if (nextBtn) nextBtn.style.display = \"none\";\n  }\n}\n\nfunction addArrowEventsEventos() {\n  const prevBtn = document.getElementById(\"prevBtnEventos\");\n  const nextBtn = document.getElementById(\"nextBtnEventos\");\n  if (prevBtn) {\n    prevBtn.addEventListener(\"click\", () => {\n      if (imagenesEventos.length > 0) {\n        currentIndexEventos = (currentIndexEventos - 1 + imagenesEventos.length) % imagenesEventos.length;\n        mostrarImagenEventos(currentIndexEventos);\n      }\n    });\n  }\n  if (nextBtn) {\n    nextBtn.addEventListener(\"click\", () => {\n      if (imagenesEventos.length > 0) {\n        currentIndexEventos = (currentIndexEventos + 1) % imagenesEventos.length;\n        mostrarImagenEventos(currentIndexEventos);\n      }\n    });\n  }\n}\n\n\n  function eliminarImagenEventos(index) {\n    const urlAEliminar = imagenesEventos[index];\n    imagenesEventos.splice(index, 1);\n    const inputs = imagenesContainerEventos.querySelectorAll(\"input[name='imagen_url_eventos[]']\");\n    inputs.forEach(input => {\n      if (input.value.trim() === urlAEliminar) {\n        input.parentElement.remove();\n      }\n    });\n    if (currentIndexEventos >= imagenesEventos.length) {\n      currentIndexEventos = imagenesEventos.length - 1;\n    }\n    actualizarVistaEventos();\n    if (imagenesEventos.length > 0) {\n      mostrarImagenEventos(currentIndexEventos);\n    }\n  }function actualizarVistaEventos() {\n  const inputs = imagenesContainerEventos.querySelectorAll(\"input[name='imagen_url_eventos[]']\");\n  imagenesEventos = [];\n  let totalValidas = 0;\n\n  inputs.forEach(input => {\n    const url = input.value.trim();\n    const errorMsg = input.nextElementSibling;\n\n    if (url !== \"\" && url.startsWith(dominioPermitidoEventos)) {\n      totalValidas++;\n      imagenesEventos.push(url);\n      errorMsg.textContent = \"\";\n    } else if (url !== \"\" && !url.startsWith(dominioPermitidoEventos)) {\n      errorMsg.textContent = \"&#x274c; Seleccione links correctos de WordPress\";\n    } else {\n      errorMsg.textContent = \"\";\n    }\n  });\n\n  contadorEventos.textContent = \"Im\u00e1genes seleccionadas: \" + totalValidas;\n\n  if (totalValidas > 0) {\n    \/\/ Animaci\u00f3n igual que publicaciones\n    formEventos.classList.add(\"slide-left\");\n    previewContainerEventos.style.display = \"block\";\n    setTimeout(() => previewContainerEventos.classList.add(\"visible\"), 50);\n\n    if (currentIndexEventos < 0) currentIndexEventos = 0;\n    if (currentIndexEventos >= imagenesEventos.length) currentIndexEventos = imagenesEventos.length - 1;\n    mostrarImagenEventos(currentIndexEventos);\n  } else {\n    \/\/ Reset animaciones si no hay im\u00e1genes v\u00e1lidas\n    formEventos.classList.remove(\"slide-left\");\n    previewContainerEventos.classList.remove(\"visible\");\n    previewContainerEventos.style.display = \"none\";\n  }\n}\n\n \/\/ Campos din\u00e1micos y validaci\u00f3n de URLs\n\nimagenesContainerEventos.addEventListener(\"input\", (e) => {\n  if (e.target.name === \"imagen_url_eventos[]\") {\n    const url = e.target.value.trim();\n    const errorMsg = e.target.nextElementSibling;\n\n    if (url !== \"\" && !url.startsWith(dominioPermitidoEventos)) {\n      errorMsg.textContent = \"&#x274c; Seleccione links correctos de WordPress\";\n    } else {\n      errorMsg.textContent = \"\";\n    }\n\n    const inputs = imagenesContainerEventos.querySelectorAll(\"input[name='imagen_url_eventos[]']\");\n    const ultimo = inputs[inputs.length - 1];\n    if (\n      ultimo.value.trim() !== \"\" &&\n      ultimo.value.startsWith(dominioPermitidoEventos) &&\n      inputs.length < maxImagenesEventos\n    ) {\n      const nuevoCampo = document.createElement(\"div\");\n      nuevoCampo.classList.add(\"image-field\");\n      nuevoCampo.innerHTML = `\n        <input type=\"text\" name=\"imagen_url_eventos[]\" placeholder=\"Pega aqu\u00ed la URL de la imagen\" autocomplete=\"off\">\n        <div class=\"error-msg\"><\/div>\n      `;\n      imagenesContainerEventos.appendChild(nuevoCampo);\n    }\n  }\n\n  actualizarVistaEventos();\n});\n\n\/\/ Eliminar campo vac\u00edo si no es el \u00faltimo\nimagenesContainerEventos.addEventListener(\"blur\", (e) => {\n  if (e.target.name === \"imagen_url_eventos[]\" && e.target.value.trim() === \"\") {\n    const inputs = imagenesContainerEventos.querySelectorAll(\"input[name='imagen_url_eventos[]']\");\n    if (inputs.length > 1 && e.target === inputs[inputs.length - 2]) {\n      e.target.parentElement.remove();\n    }\n    actualizarVistaEventos();\n  }\n}, true);\n\n  \/\/ Guardar o actualizar evento\n formEventos.addEventListener(\"submit\", async (e) => {\n  e.preventDefault();\n  mensajeErrorEventos.style.display = \"none\";\n  mensajeErrorEventos.textContent = \"\";\n\n  const titulo = document.getElementById(\"evento-titulo\").value.trim();\n  const descripcion = document.getElementById(\"evento-descripcion\").value.trim();\n  const imagenesValidas = imagenesEventos.filter(url => url.startsWith(dominioPermitidoEventos));\n\n  if (titulo === \"\" || descripcion === \"\") {\n    mensajeErrorEventos.style.display = \"block\";\n    mensajeErrorEventos.innerHTML = \"&#x274c; Por favor llene todos los campos obligatorios (T\u00edtulo y Descripci\u00f3n).\";\n    return;\n  }\n\n  \/\/ Imagen opcional: advertencia si no hay ninguna\n  if (imagenesValidas.length === 0) {\n    const confirmar = confirm(\"&#x26a0;&#xfe0f; \u00bfSeguro que no quieres subir ninguna imagen?\");\n    if (!confirmar) return;\n  }\n\nlet data, error;\nconst imagenFinal = imagenesValidas[0] || null; \/\/ solo se guarda una imagen\nconst fecha = document.getElementById(\"evento-fecha\").value;\nconst hora = document.getElementById(\"evento-hora\").value;\n\nif (idEvento) {\n  const idNum = parseInt(idEvento, 10);\n  ({ data, error } = await sb\n    .from(\"eventos\")\n    .update({ titulo, descripcion, fecha, hora, imagen: imagenFinal })\n    .eq(\"id\", idNum));\n} else {\n  ({ data, error } = await sb\n    .from(\"eventos\")\n    .insert([{ titulo, descripcion, fecha, hora, imagen: imagenFinal }]));\n}\n\nif (error) {\n  mensajeErrorEventos.style.display = \"block\";\n  mensajeErrorEventos.innerHTML = \"&#x274c; Error al guardar: \" + error.message;\n} else {\n  alert(\"&#x2705; Evento \" + (idEvento ? \"actualizado\" : \"guardado\") + \" correctamente\");\n  if (idEvento) {\n    window.location.href = \"https:\/\/utelvt.edu.ec\/biblioteca\/noticias-biblioteca\/\";\n  } else {\n    resetFormularioEventos();\n  }\n}\n});\n\nwindow.addEventListener(\"load\", resetFormularioEventos);\nwindow.addEventListener(\"pageshow\", (event) => {\n  if (event.persisted) resetFormularioEventos();\n});\n\nactualizarVistaEventos();\n\n\/\/ Cargar datos si venimos con ?id= en la URL\nasync function cargarEdicionEventos() {\n  if (!idEvento) return;\n\n  const idNum = parseInt(idEvento, 10);\n  const { data, error } = await sb\n    .from(\"eventos\")\n    .select(\"*\")\n    .eq(\"id\", idNum)\n    .single();\n\n  if (error) {\n    console.error(\"&#x274c; Error al cargar evento:\", error.message);\n    return;\n  }\n  if (!data) {\n    console.warn(\"&#x26a0;&#xfe0f; No se encontr\u00f3 evento con id:\", idNum);\n    return;\n  }\n\n  \/\/ Rellenar campos de texto\n  document.getElementById(\"evento-titulo\").value = data.titulo || \"\";\n  document.getElementById(\"evento-descripcion\").value = data.descripcion || \"\";\n  document.getElementById(\"evento-fecha\").value = data.fecha || \"\";\n  document.getElementById(\"evento-hora\").value = data.hora || \"\";\n\n  \/\/ Rellenar im\u00e1genes (solo una en tu tabla)\n  imagenesContainerEventos.innerHTML = \"\";\n  imagenesEventos = data.imagen ? [data.imagen] : [];\n\n  imagenesEventos.forEach(url => {\n    const campo = document.createElement(\"div\");\n    campo.classList.add(\"image-field\");\n    campo.innerHTML = `\n      <input type=\"text\" name=\"imagen_url_eventos[]\" value=\"${url}\" autocomplete=\"off\">\n      <div class=\"error-msg\"><\/div>\n    `;\n    imagenesContainerEventos.appendChild(campo);\n  });\n\n  \/\/ A\u00f1adir un campo vac\u00edo si no se alcanz\u00f3 el m\u00e1ximo\n  if (imagenesEventos.length < maxImagenesEventos) {\n    const nuevoCampo = document.createElement(\"div\");\n    nuevoCampo.classList.add(\"image-field\");\n    nuevoCampo.innerHTML = `\n      <input type=\"text\" name=\"imagen_url_eventos[]\" placeholder=\"Pega aqu\u00ed la URL de la imagen\" autocomplete=\"off\">\n      <div class=\"error-msg\"><\/div>\n    `;\n    imagenesContainerEventos.appendChild(nuevoCampo);\n  }\n\n  \/\/ Actualizar contador y vista\n  contadorEventos.textContent = \"Im\u00e1genes seleccionadas: \" + imagenesEventos.length;\n  currentIndexEventos = 0;\n  actualizarVistaEventos();\n}\n\n\/\/ Ejecutar carga al iniciar\ndocument.addEventListener(\"DOMContentLoaded\", cargarEdicionEventos);\n\n<\/script>\n\n\n\n<script>\n\/\/ Portada: elementos\nconst modoAuto = document.getElementById(\"modo-auto\");\nconst modoOrganizar = document.getElementById(\"modo-organizar\");\nconst organizarPortadas = document.getElementById(\"organizar-portadas\");\nconst buscadorPortadas = document.getElementById(\"buscador-portadas\");\nconst btnBuscarPortadas = document.getElementById(\"btn-buscar-portadas\");\nconst resultadosPortadas = document.getElementById(\"resultados-portadas\");\nconst seleccionadas = document.getElementById(\"portadas-seleccionadas\");\nconst guardarPortadas = document.getElementById(\"guardar-portadas\");\nconst mensajePortadas = document.getElementById(\"mensaje-portadas\");\n\nlet portadaSeleccionada = null;\n\n\/\/ Modo autom\u00e1tico\nmodoAuto.addEventListener(\"click\", async () => {\n  organizarPortadas.style.display = \"none\";\n  mensajePortadas.textContent = \"Portadas se mostrar\u00e1n en orden autom\u00e1tico.\";\n  mensajePortadas.style.color = \"#008000\";\n  mensajePortadas.style.fontWeight = \"normal\";\n  portadaSeleccionada = null;\n\n  modoAuto.classList.add(\"active\");\n  modoOrganizar.classList.remove(\"active\");\n\n  \/\/ Eliminar todas las prioridades de la tabla\n  const { error } = await sb\n    .from(\"noticias_prioritarias\")\n    .delete()\n    .not(\"noticia_id\", \"is\", null);\n\n  if (error) {\n    console.error(\"Error al limpiar prioridades:\", error);\n    mensajePortadas.textContent = \"\u00a1Error! No se pudo limpiar prioridades.\";\n    mensajePortadas.style.color = \"#a40000\";\n    mensajePortadas.style.fontWeight = \"bold\";\n    return;\n  }\n\n  mensajePortadas.textContent = \"&#x2705; Se elimin\u00f3 la portada prioritaria. Ahora se muestran en orden normal.\";\n  mensajePortadas.style.color = \"#008000\";\n  mensajePortadas.style.fontWeight = \"normal\";\n});\n\n\/\/ Modo organizar\nmodoOrganizar.addEventListener(\"click\", () => {\n  organizarPortadas.style.display = \"block\";\n  mensajePortadas.textContent = \"\";\n\n  \/\/ Resetear selecci\u00f3n al entrar en modo organizar\n  portadaSeleccionada = null;\n  seleccionadas.innerHTML = \"\";\n  resultadosPortadas.innerHTML = \"\";\n\n  modoOrganizar.classList.add(\"active\");\n  modoAuto.classList.remove(\"active\");\n});\n\n\/\/ Buscar noticias en Supabase (tabla imagenes_portada)\nbtnBuscarPortadas.addEventListener(\"click\", async () => {\n  const query = buscadorPortadas.value.trim();\n  resultadosPortadas.innerHTML = \"\";\n\n  if (query.length < 3) {\n    resultadosPortadas.textContent = \"\u00a1Precauci\u00f3n! Escriba al menos 3 caracteres.\";\n    resultadosPortadas.style.color = \"#e6b800\";\n    resultadosPortadas.style.fontWeight = \"bold\";\n    return;\n  }\n\n  let { data, error } = await sb\n    .from(\"imagenes_portada\")\n    .select(\"id, titulo, imagenes, created_at\")\n    .ilike(\"titulo\", `%${query}%`);\n\n  if (error) {\n    console.error(\"Error Supabase:\", error);\n    resultadosPortadas.textContent = \"\u00a1Error! No se pudo buscar en la base de datos.\";\n    resultadosPortadas.style.color = \"#a40000\";\n    resultadosPortadas.style.fontWeight = \"bold\";\n    return;\n  }\n\n  if (!data || data.length === 0) {\n    resultadosPortadas.textContent = \"\u00a1Precauci\u00f3n! No se encontraron resultados.\";\n    resultadosPortadas.style.color = \"#e6b800\";\n    resultadosPortadas.style.fontWeight = \"bold\";\n    return;\n  }\n\n  \/\/ Mostrar resultados con radio button (solo una portada)\n  data.forEach(noticia => {\n    const div = document.createElement(\"div\");\n    div.classList.add(\"resultado-item\");\n\n    const radio = document.createElement(\"input\");\n    radio.type = \"radio\";\n    radio.name = \"portada\";\n    radio.id = `radio-${noticia.id}`;\n\n    const miniatura = noticia.imagenes && noticia.imagenes.length > 0 ? noticia.imagenes[0] : \"\";\n    const fechaHora = new Date(noticia.created_at).toLocaleString();\n\n    const label = document.createElement(\"label\");\n    label.setAttribute(\"for\", `radio-${noticia.id}`);\n    label.innerHTML = `\n      <div style=\"display:grid;grid-template-columns:2fr 1fr;align-items:center;gap:10px;\">\n        <div>\n          <strong>${noticia.titulo}<\/strong><br>\n          <small>${fechaHora}<\/small>\n        <\/div>\n        <div>\n          <img decoding=\"async\" src=\"${miniatura}\" alt=\"Miniatura\" style=\"width:100px;height:auto;border-radius:4px;\">\n        <\/div>\n      <\/div>\n    `;\n\n    radio.addEventListener(\"change\", () => {\n      if (radio.checked) {\n        portadaSeleccionada = noticia;\n        actualizarSeleccionada();\n      }\n    });\n\n    div.appendChild(radio);\n    div.appendChild(label);\n    resultadosPortadas.appendChild(div);\n  });\n});\n\n\/\/ Mostrar portada seleccionada con bot\u00f3n de eliminar\nfunction actualizarSeleccionada() {\n  seleccionadas.innerHTML = \"\";\n  if (portadaSeleccionada) {\n    const li = document.createElement(\"li\");\n    li.classList.add(\"seleccionada-item\");\n\n    const titulo = document.createElement(\"span\");\n    titulo.textContent = portadaSeleccionada.titulo;\n\n    const btnEliminar = document.createElement(\"span\");\n    btnEliminar.textContent = \"x\";\n    btnEliminar.classList.add(\"btn-eliminar\");\n\n    btnEliminar.addEventListener(\"click\", () => {\n      portadaSeleccionada = null;\n      document.querySelectorAll(\"input[name='portada']\").forEach(r => r.checked = false);\n      actualizarSeleccionada();\n    });\n\n    li.appendChild(titulo);\n    li.appendChild(btnEliminar);\n    seleccionadas.appendChild(li);\n  }\n}\n\n\/\/ Guardar portada\nguardarPortadas.addEventListener(\"click\", async () => {\n  if (!portadaSeleccionada) {\n    mensajePortadas.textContent = \"\u00a1Error! Debes seleccionar una portada.\";\n    mensajePortadas.style.color = \"#a40000\";\n    mensajePortadas.style.fontWeight = \"bold\";\n    return;\n  }\n\n  \/\/ Guardar en Supabase (tabla noticias_prioritarias)\n  const { error } = await sb.from(\"noticias_prioritarias\").upsert([\n    {\n      noticia_id: portadaSeleccionada.id,\n      usuario_admin: 1 \/\/ aqu\u00ed pones el id del admin si lo manejas\n    }\n  ]);\n\n  if (error) {\n    mensajePortadas.textContent = \"\u00a1Error! No se pudo guardar en la base de datos.\";\n    mensajePortadas.style.color = \"#a40000\";\n    mensajePortadas.style.fontWeight = \"bold\";\n    return;\n  }\n\n  \/\/ Mensaje de \u00e9xito\n  mensajePortadas.textContent = \"&#x2705; Portada principal guardada correctamente.\";\n  mensajePortadas.style.color = \"#008000\";\n  mensajePortadas.style.fontWeight = \"normal\";\n\n  \/\/ Limpieza\n  buscadorPortadas.value = \"\";\n  resultadosPortadas.innerHTML = \"\";\n  seleccionadas.innerHTML = \"\";\n  portadaSeleccionada = null;\n});\n<\/script>\n\n\n<script>\n  \/\/ Validar sesi\u00f3n y permisos de administrador\n  document.addEventListener(\"DOMContentLoaded\", async () => {\n    const { data: { user }, error: userError } = await sb.auth.getUser();\n\n    if (userError) {\n      console.error(\"Error al obtener usuario:\", userError);\n    }\n\n    if (user) {\n      \/\/ Verificar si el correo est\u00e1 en la tabla administradores\n      const { data, error } = await sb\n        .from(\"administradores\")\n        .select(\"email\")\n        .eq(\"email\", user.email)\n        .single();\n\n      if (error) {\n        console.error(\"Error al verificar administrador:\", error);\n      }\n\n      if (data) {\n        \/\/ &#x2705; Es administrador \u2192 mostrar panel\n        document.getElementById(\"panel-admin-noticias\").style.display = \"block\";\n        console.log(\"Usuario administrador:\", user.email);\n      } else {\n        \/\/ &#x274c; No es administrador \u2192 ocultar panel\n        document.getElementById(\"panel-admin-noticias\").style.display = \"none\";\n        alert(\"No tienes permisos para administrar noticias.\");\n      }\n    } else {\n      \/\/ No hay sesi\u00f3n \u2192 redirigir a login de noticias\n      window.location.href = \"https:\/\/utelvt.edu.ec\/biblioteca\/noticias-biblioteca\/\";\n    }\n  });\n<\/script>\n\n<script>\n  const btnAdmins = document.getElementById(\"btn-admins\");\n  const sectionAdmins = document.getElementById(\"section-admins\");\n  const adminsForm = document.getElementById(\"admins-form\");\n  const adminsUl = document.getElementById(\"admins-ul\");\n\n  btnAdmins.addEventListener(\"click\", () => {\n    document.querySelectorAll(\".section\").forEach(sec => sec.classList.remove(\"active\"));\n    sectionAdmins.classList.add(\"active\");\n    cargarAdmins();\n  });\n\n  \/\/ Cargar lista de administradores\n  async function cargarAdmins() {\n    const { data, error } = await sb.from(\"administradores\").select(\"id, email\");\n    if (error) {\n      console.error(\"Error al cargar administradores:\", error);\n      return;\n    }\n    adminsUl.innerHTML = \"\";\n    data.forEach(admin => {\n      const li = document.createElement(\"li\");\n      li.innerHTML = `\n        <span>${admin.email}<\/span>\n        <div class=\"admin-actions\">\n          <button class=\"admin-btn edit\">Editar<\/button>\n          <button class=\"admin-btn delete\">Eliminar<\/button>\n        <\/div>\n      `;\n\n      \/\/ Eliminar administrador (excepto mauricio.garces@utelvt.edu.ec)\n      li.querySelector(\".delete\").addEventListener(\"click\", async () => {\n        if (admin.email === \"mauricio.garces@utelvt.edu.ec\") {\n          alert(\"&#x274c; Este administrador no puede ser eliminado.\");\n          return;\n        }\n        const { error } = await sb.from(\"administradores\").delete().eq(\"id\", admin.id);\n        if (error) {\n          alert(\"&#x274c; Error al eliminar: \" + error.message);\n          return;\n        }\n        alert(\"&#x2705; Administrador eliminado.\");\n        cargarAdmins();\n      });\n\n      \/\/ Editar administrador (excepto mauricio.garces@utelvt.edu.ec)\n      li.querySelector(\".edit\").addEventListener(\"click\", async () => {\n        if (admin.email === \"mauricio.garces@utelvt.edu.ec\") {\n          alert(\"&#x274c; Este administrador no puede ser editado.\");\n          return;\n        }\n        const nuevoEmail = prompt(\"Ingrese el nuevo correo:\", admin.email);\n        if (!nuevoEmail) return;\n\n        \/\/ Validar dominio permitido\n        const regex = \/(@gmail\\.com|@utelvt\\.edu\\.ec)$\/i;\n        if (!regex.test(nuevoEmail)) {\n          alert(\"&#x274c; Escriba un correo v\u00e1lido de Gmail o terminado en @utelvt.edu.ec\");\n          return;\n        }\n\n        const { error } = await sb.from(\"administradores\").update({ email: nuevoEmail }).eq(\"id\", admin.id);\n        if (error) {\n          alert(\"&#x274c; Error al actualizar: \" + error.message);\n          return;\n        }\n        alert(\"&#x2705; Administrador actualizado.\");\n        cargarAdmins();\n      });\n\n      adminsUl.appendChild(li);\n    });\n  }\n\n  \/\/ A\u00f1adir nuevo administrador\n  adminsForm.addEventListener(\"submit\", async (e) => {\n    e.preventDefault();\n    const email = document.getElementById(\"admin-email\").value.trim();\n    if (!email) return;\n\n    \/\/ Validar dominio permitido\n    const regex = \/(@gmail\\.com|@utelvt\\.edu\\.ec)$\/i;\n    if (!regex.test(email)) {\n      alert(\"&#x274c; Escriba un correo v\u00e1lido de Gmail o terminado en @utelvt.edu.ec\");\n      return;\n    }\n\n    const { error } = await sb.from(\"administradores\").insert([{ email }]);\n    if (error) {\n      alert(\"&#x274c; Error al a\u00f1adir administrador: \" + error.message);\n      return;\n    }\n\n    alert(\"&#x2705; Administrador a\u00f1adido correctamente.\");\n    document.getElementById(\"admin-email\").value = \"\";\n    cargarAdmins();\n  });\n<\/script>\n\n<script>\n\/\/ Variable global para saber si es administrador\nlet esAdmin = false;\n\n\/\/ Funci\u00f3n que consulta Supabase y actualiza esAdmin\nasync function verificarAdmin() {\n  const { data: { user } } = await sb.auth.getUser();\n\n  if (!user) {\n    \/\/ No hay sesi\u00f3n \u2192 redirigir\n    window.location.href = \"https:\/\/utelvt.edu.ec\/biblioteca\/noticias-biblioteca\/\";\n    return;\n  }\n\n  \/\/ Verificar si est\u00e1 en la tabla administradores\n  const { data, error } = await sb\n    .from(\"administradores\")\n    .select(\"email\")\n    .eq(\"email\", user.email);\n\n  if (error || !data || data.length === 0) {\n    \/\/ No es administrador \u2192 bloquear acceso\n    alert(\"Acceso denegado. Solo administradores pueden entrar.\");\n    window.location.href = \"https:\/\/utelvt.edu.ec\/biblioteca\/noticias-biblioteca\/\";\n    return;\n  }\n\n  \/\/ Si llega aqu\u00ed \u2192 es administrador\n  esAdmin = true;\n  console.log(\"Acceso concedido a administrador:\", user.email);\n}\n\n\/\/ Ejecutar la verificaci\u00f3n al cargar la p\u00e1gina\ndocument.addEventListener(\"DOMContentLoaded\", verificarAdmin);\n<\/script>\n\n\n\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Administraci\u00f3n de P\u00e1gina de Noticias ADMINISTRACION DE PAGINA DE NOTICIAS Regresar a la p\u00e1gina de noticias Subir publicaciones Subir eventos Editar portada de noticias Administradores Aqu\u00ed puedes subir tus publicaciones a la p\u00e1gina de noticias T\u00edtulo Descripci\u00f3n Im\u00e1genes (m\u00e1ximo 10) Subir\/Seleccionar imagen en WordPress Im\u00e1genes seleccionadas: 0 Guardar en Base de Datos Im\u00e1genes seleccionadas Aqu\u00ed [&hellip;]<\/p>\n","protected":false},"author":3,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"footnotes":""},"class_list":["post-5787","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/utelvt.edu.ec\/biblioteca\/wp-json\/wp\/v2\/pages\/5787","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/utelvt.edu.ec\/biblioteca\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/utelvt.edu.ec\/biblioteca\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/utelvt.edu.ec\/biblioteca\/wp-json\/wp\/v2\/users\/3"}],"replies":[{"embeddable":true,"href":"https:\/\/utelvt.edu.ec\/biblioteca\/wp-json\/wp\/v2\/comments?post=5787"}],"version-history":[{"count":119,"href":"https:\/\/utelvt.edu.ec\/biblioteca\/wp-json\/wp\/v2\/pages\/5787\/revisions"}],"predecessor-version":[{"id":6323,"href":"https:\/\/utelvt.edu.ec\/biblioteca\/wp-json\/wp\/v2\/pages\/5787\/revisions\/6323"}],"wp:attachment":[{"href":"https:\/\/utelvt.edu.ec\/biblioteca\/wp-json\/wp\/v2\/media?parent=5787"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}