1import { checkUserLogged } from "../auth/checkUserLogged.js"; 2import { ensureUploadModal, openUploadModal } from "../slider/upload/index.js"; 3import { ensureEditModal, openEditModal } from "../slider/manage/index.js"; 4import { API_URL, SITE, LIST, ASSETS_BASE } from "../slider/config.js"; 5 6document.addEventListener("DOMContentLoaded", async () => { 7 const data = await getSliderHome().catch((err) => { 8 console.error("[SliderHome] Falla getSliderHome:", err); 9 return []; // fallback 10 }); 11 12 const listEl = document.querySelector(".slider__home .splide__list"); 13 if (!listEl) return; 14 15 const isEmpty = !Array.isArray(data) || data.length === 0; 16 17 if (isEmpty) { 18 listEl.appendChild(createDefaultSlide()); 19 } else { 20 for (const item of data) listEl.appendChild(sliderHomeComponent(item)); 21 } 22 23 const nextOrder = !isEmpty 24 ? Math.max( 25 ...data.map((d) => 26 Number.isFinite(Number(d.orden ?? d.Orden)) 27 ? Number(d.orden ?? d.Orden) 28 : 0 29 ) 30 ) + 1 31 : 1; 32 33 try { 34 const currentUser = await checkUserLogged(); 35 if (currentUser) { 36 ensureUploadModal(); 37 ensureEditModal(); 38 39 const root = document.querySelector(".slider__home"); 40 41 const editBtn = document.createElement("button"); 42 editBtn.type = "button"; 43 editBtn.title = "Administrar Slider"; 44 editBtn.setAttribute("aria-label", "Administrar Slider"); 45 editBtn.className = "setting-btn absolute top-3 right-3 z-50"; 46 editBtn.innerHTML = ` 47 <span class="bar bar1"></span> 48 <span class="bar bar2"></span> 49 <span class="bar bar1"></span> 50 `; 51 root.appendChild(editBtn); 52 53 const fab = createFabMenu(); 54 root.appendChild(fab.container); 55 56 let fabOpen = false; 57 const toggleFab = (force) => { 58 fabOpen = typeof force === "boolean" ? force : !fabOpen; 59 fab.container.classList.toggle("opacity-0", !fabOpen); 60 fab.container.classList.toggle("pointer-events-none", !fabOpen); 61 fab.container.classList.toggle("translate-y-2", !fabOpen); 62 editBtn.innerHTML = fabOpen 63 ? `<img src="${ASSETS_BASE}/slider-home/img/cancel-close-svgrepo-com.svg" alt="Cerrar" class="w-6 h-6" />` 64 : ` 65 <span class="bar bar1"></span> 66 <span class="bar bar2"></span> 67 <span class="bar bar1"></span> 68 `; 69 }; 70 71 editBtn.addEventListener("click", (e) => { 72 e.stopPropagation(); 73 toggleFab(); 74 }); 75 76 document.addEventListener("click", (e) => { 77 if ( 78 fabOpen && 79 !fab.container.contains(e.target) && 80 e.target !== editBtn 81 ) { 82 toggleFab(false); 83 } 84 }); 85 86 fab.uploadBtn.addEventListener("click", () => { 87 toggleFab(false); 88 openUploadModal(nextOrder); 89 }); 90 91 fab.manageBtn.addEventListener("click", () => { 92 toggleFab(false); 93 openEditModal(); 94 }); 95 } 96 } catch (e) { 97 console.warn("[SliderHome] No se pudo habilitar modo admin:", e); 98 } 99 100 // ======= Splide con detección de slider vacÃo ======= 101 const isEmptySlider = !Array.isArray(data) || data.length === 0; 102 103 const splideHome = new Splide(".slider__home", { 104 type: "loop", 105 perPage: 1, 106 perMove: 1, 107 autoplay: true, 108 interval: 5000, 109 classes: { 110 arrows: "splide__arrows", 111 arrow: "rounded-full", 112 prev: "splide__arrow--prev absolute !left-5 top-1/2 transform -translate-y-1/2 scale-x-[1] min-w-[2.2rem] h-[2.2rem] border-0 flex items-center justify-center bg-gray-200 shadow-sm focus:!outline-none [&>svg]:w-[1.3rem]", 113 next: "splide__arrow--next absolute !right-5 top-1/2 transform -translate-y-1/2 min-w-[2.2rem] h-[2.2rem] border-0 flex items-center justify-center shadow-sm focus:!outline-none [&>svg]:w-[1.3rem] bg-gray-200", 114 pagination: "splide__pagination flex gap-1", 115 page: "splide__pagination__page !bg-gray-400 w-3 h-3", 116 }, 117 rewind: true, 118 autoplay: "play", 119 }); 120 121 // Contador (si existe) 122 splideHome.on("mounted move", function () { 123 const el = document.getElementById("splide-counter"); 124 if (!el) return; 125 126 if (isEmptySlider) { 127 el.textContent = `0 / 0`; 128 } else { 129 const totalSlides = splideHome.Components.Slides.getLength(); 130 const currentSlide = splideHome.index + 1; 131 el.textContent = `${currentSlide} / ${totalSlides}`; 132 } 133 }); 134 135 splideHome.mount(); 136}); 137 138/** 139 * Obtiene los Ãtems del slider con manejo robusto de formatos de respuesta. 140 * Nunca lanza excepción hacia arriba; retorna [] en caso de error. 141 */ 142const getSliderHome = async () => { 143 // Asegurar que el endpoint tenga ref=get-list-items cuando aplica 144 const url = API_URL.includes("ref=get-list-items") 145 ? API_URL 146 : API_URL.includes("?") 147 ? `${API_URL}
147&ref=get-list-items` 148 : `${API_URL}?ref=get-list-items`; 149 150 const body = { 151 fields: ["Title", "FileRef", "Url", "Orden", "Publicado"], 152 site: SITE, 153 list: LIST, 154 }; 155 156 try { 157 const response = await fetch(url, { 158 method: "POST", 159 headers: { 160 "Content-Type": "application/json", 161 Accept: "application/json", 162 }, 163 body: JSON.stringify(body), 164 }); 165 166 if (!response.ok) { 167 console.error("[SliderHome] HTTP", response.status, response.statusText); 168 return []; 169 } 170 171 const raw = await response.json().catch((e) => { 172 console.error("[SliderHome] JSON parse error:", e); 173 return null; 174 }); 175 if (!raw) return []; 176 177 // Normalizar formatos posibles 178 let items = []; 179 if (Array.isArray(raw)) { 180 items = raw; 181 } else if (raw?.data && Array.isArray(raw.data)) { 182 items = raw.data; 183 } else if (raw?.status === "ok" && Array.isArray(raw?.data)) { 184 items = raw.data; 185 } else if (raw?.d?.results && Array.isArray(raw.d.results)) { 186 items = raw.d.results; // SharePoint clásico 187 } else { 188 console.warn("[SliderHome] Respuesta no reconocida:", raw); 189 return []; 190 } 191 192 // Filtrar/ordenar defensivo 193 return items 194 .filter((it) => { 195 // Publicado puede venir como bool, 'true' o 1 196 const pub = it.Publicado; 197 return pub === true || pub === "true" || pub === 1 || pub === "1"; 198 }) 199 .sort((a, b) => (Number(a.Orden) || 0) - (Number(b.Orden) || 0)); 200 } catch (err) { 201 console.error("[SliderHome] Error fetching data:", err); 202 return []; 203 } 204}; 205 206// Slide con fondo blur + imagen principal y alturas responsivas 207const sliderHomeComponent = (item) => { 208 const li = document.createElement("li"); 209 li.className = "splide__slide"; 210 li.innerHTML = ` 211 <a href="${item.Url || "#"}" class="w-full relative md:h-[500px] h-[300px]"> 212 <img 213 src="${item.FileRef}" 214 alt="${item.Title || "Slider Image"}" 215 class="w-full object-cover absolute blur-lg md:h-[500px] h-[300px]" /> 216 <img 217 src="${item.FileRef}" 218 alt="${item.Title || "Slider Image"}" 219 class="w-full h-full object-contain relative" /> 220 </a>`; 221 return li; 222}; 223 224// Slide por defecto (estado vacÃo) â diseño sobrio, sin fondo/efectos 225 226const createDefaultSlide = () => { 227 const li = document.createElement("li"); 228 li.className = "splide__slide"; 229 li.innerHTML = ` 230 <div class="w-full h-full flex items-center justify-center bg-gradient-to-br from-blue-50 to-indigo-100 relative overflow-hidden md:h-[500px] h-[300px]"> 231 <div class="absolute top-10 left-10 text-blue-200 opacity-20"><i class="fas fa-images text-6xl"></i></div> 232 <div class="absolute bottom-10 right-10 text-indigo-200 opacity-20"><i class="fas fa-camera text-5xl"></i></div> 233 <div class="absolute top-1/3 right-1/4 text-blue-100 opacity-15"><i class="fas fa-photo-video text-4xl"></i></div> 234 <div class="text-center p-8 max-w-md mx-auto z-10 relative"> 235 <div class="mb-6"> 236 <div class="inline-flex items-center justify-center w-20 h-20 bg-white rounded-full shadow-lg mb-4"> 237 <i class="fas fa-image text-3xl text-gray-400"></i> 238 </div> 239 </div> 240 <h2 class="text-3xl text-gray-700 font-bold mb-4 leading-tight">Por el momento no tenemos publicaciones</h2> 241 <p class="text-gray-600 text-lg mb-6 leading-relaxed">Estamos trabajando para traerte contenido increÃble muy pronto</p> 242 <div class="flex items-center justify-center text-gray-500"> 243 <i class="fas fa-clock mr-2"></i> 244 <span class="text-sm">Mantente atento a las novedades</span> 245 </div> 246 </div> 247 </div>`; 248 return li; 249}; 250 251const createFabMenu = () => { 252 const container = document.createElement("div"); 253 container.className = 254 "absolute top-[4rem] right-3 z-[9999] flex flex-col gap-2 transition-all duration-200 opacity-0 pointer-events-none translate-y-2"; 255 256 const uploadBtn = document.createElement("button"); 257 uploadBtn.type = "button"; 258 uploadBtn.className = 259 "p-2 rounded-full bg-blue-600 text-white shadow-lg hover:bg-blue-700 border border-solid border-blue-700 cursor-pointer"; 260 uploadBtn.innerHTML = ` 261 <img 262 src="${ASSETS_BASE}/slider-home/img/upload-svgrepo-com.svg" 263 alt="Subir" 264 class="w-6 h-6" 265 /> 266 `; 267 268 const manageBtn = document.createElement("button"); 269 manageBtn.type = "button"; 270 manageBtn.className = 271 "p-2 rounded-full bg-gray-800 text-white shadow-lg hover:bg-gray-900 border border-solid border-gray-900 cursor-pointer"; 272 manageBtn.innerHTML = ` 273 <img 274 src="${ASSETS_BASE}/slider-home/img/pencil-svgrepo-com.svg" 275 alt="Editar" 276 class="w-6 h-6" 277 /> 278 `; 279 280 container.appendChild(uploadBtn); 281 container.appendChild(manageBtn); 282 return { container, uploadBtn, manageBtn }; 283};
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.