PageSourceSearch

https://zonabananera-magdalena.gov.co/SiteAssets/v1/assets/components/home/slider-home/js/app/main.js

js zonabananera-magdalena.gov.co collected 2026-10-02 16:59:38 UTC 10,040 bytes, 283 lines download raw bytes

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.