// merged-script.js let originalTasks = []; function getCompletedTasksFromCookies() { const cookie = document.cookie.split('; ').find(row => row.startsWith('completedTasks=')); return cookie ? JSON.parse(decodeURIComponent(cookie.split('=')[1])) : {}; } function saveCompletedTasksToCookies(completedTasks) { const expires = new Date(Date.now() + 30 * 24 * 60 * 60 * 1000); document.cookie = `completedTasks=${encodeURIComponent(JSON.stringify(completedTasks))}; expires=${expires.toUTCString()}; path=/`; } function getCourseColor(course) { const isDark = document.body.classList.contains("dark-mode"); const lightColors = { "APLICACIONS PER A DISPOSITIUS MÒBILS (105025-2425)": "#007bff", "COMPUTACIÓ DISTRIBUÏDA I APLICACIONS (105027-2425)": "#dc3545", "XARXES I COMUNICACIONS (105024-2425)": "#dfb613", "Lengua": "#20c997", "Historia": "#6f42c1" }; const darkColors = { "APLICACIONS PER A DISPOSITIUS MÒBILS (105025-2425)": "#4da3ff", "COMPUTACIÓ DISTRIBUÏDA I APLICACIONS (105027-2425)": "#ff6b6b", "XARXES I COMUNICACIONS (105024-2425)": "#ffe066", "Lengua": "#63f2c4", "Historia": "#c79aff" }; const colors = isDark ? darkColors : lightColors; return colors[course] || (isDark ? "black" : "#0d6efd"); } async function fetchData() { const API_BASE_URL = window.location.href + "/api"; try { const cacheData = localStorage.getItem("data"); const cacheLocation = localStorage.getItem("cacheLocation"); const now = Date.now(); const pathSegments = window.location.pathname.split("/").filter(Boolean); const currentLocation = pathSegments[0] || "default"; let data; if (cacheData && cacheLocation === currentLocation && now - localStorage.getItem("cacheTime") < 50000) { data = JSON.parse(cacheData); } else { const response = await fetch(`${API_BASE_URL}/data`); if (!response.ok) throw new Error(`HTTP Error! Status: ${response.status}`); data = await response.json(); localStorage.setItem("data", JSON.stringify(data)); localStorage.setItem("cacheTime", now); localStorage.setItem("cacheLocation", currentLocation); } if (!data || !data.tasks) throw new Error("❌ Los datos de tareas no están disponibles."); const tasks = Array.isArray(data.tasks) ? data.tasks : [data.tasks]; originalTasks = tasks; renderCalendar(tasks); renderTaskList(tasks); document.getElementById("lastUpdated").textContent = `Última actualización: ${data.lastUpdated || 'N/A'}`; } catch (error) { console.error("❌ Error al obtener los datos:", error); document.getElementById("task-list").innerHTML = `
  • ❌ Error al comunicarse con la API ❌
    ${error.message || 'N/A'}
  • `; } finally { document.getElementById("loading-screen").style.display = "none"; } } function renderCalendar(tasks) { $('#calendar').fullCalendar({ locale: 'es', defaultView: 'agendaWeek', allDaySlot: false, slotDuration: '01:00:00', minTime: "07:00:00", maxTime: "24:00:00", height: 'auto', contentHeight: 'auto', firstDay: moment().isoWeekday()+1, events: tasks.map(task => ({ ...task, color: getCourseColor(task.course) })), eventClick: function (event) { if (event.url) { window.open(event.url, '_blank'); return false; } }, viewRender: function () { checkFutureTasks(tasks); } }); } function renderTaskList(tasks) { const taskList = document.getElementById("task-list"); const completedList = document.getElementById("completed-tasks"); const completedTasks = getCompletedTasksFromCookies(); taskList.innerHTML = ""; completedList.innerHTML = ""; tasks.forEach(task => { const taskId = `${task.course}-${task.title}`; const dueDate = moment(task.start); const now = moment(); const duration = moment.duration(dueDate.diff(now)); const timeText = `${Math.floor(duration.asDays())}d ${duration.hours()}h ${duration.minutes()}m`; const dueDateFormatted = dueDate.format("dddd, D [de] MMMM [de] YYYY HH:mm"); const courseColor = getCourseColor(task.course); const li = document.createElement("li"); li.className = "list-group-item d-flex justify-content-between align-items-start flex-column flex-md-row"; li.style.borderLeft = `6px solid ${courseColor}`; if (completedTasks[taskId]) { li.classList.add("task-slide-in"); const contentDiv = document.createElement("div"); contentDiv.innerHTML = ` ${task.course}
    ${task.title}
    ✔ Completado el ${completedTasks[taskId]}`; const removeBtn = document.createElement("button"); removeBtn.className = "btn btn-outline-danger btn-sm mt-2"; removeBtn.textContent = "❌"; removeBtn.title = "Quitar de completadas"; removeBtn.onclick = () => { li.classList.add("task-fade-out"); setTimeout(() => { delete completedTasks[taskId]; saveCompletedTasksToCookies(completedTasks); renderTaskList(tasks); $('#calendar').fullCalendar('removeEvents'); $('#calendar').fullCalendar('addEventSource', tasks.map(task => ({ ...task, color: getCourseColor(task.course) }))); }, 300); }; li.appendChild(contentDiv); li.appendChild(removeBtn); completedList.appendChild(li); } else { li.classList.add("task-slide-in"); const leftDiv = document.createElement("div"); leftDiv.innerHTML = ` ${task.course} - ${task.title} - ${dueDateFormatted}`; const rightDiv = document.createElement("div"); rightDiv.className = "d-flex gap-2 align-items-center justify-content-end mt-2 mt-md-0"; const completeBtn = document.createElement("button"); completeBtn.className = "btn-check-complete"; completeBtn.innerHTML = "✅"; completeBtn.title = "Marcar como completada"; completeBtn.style.border = "none"; const badge = document.createElement("span"); badge.className = "badge-time"; badge.textContent = timeText; completeBtn.onclick = (e) => { e.stopPropagation(); completeBtn.classList.add("bounce-spin-hide"); setTimeout(() => { completedTasks[taskId] = new Date().toLocaleString(); saveCompletedTasksToCookies(completedTasks); renderTaskList(tasks); $('#calendar').fullCalendar('removeEvents'); $('#calendar').fullCalendar('addEventSource', tasks.map(task => ({ ...task, color: getCourseColor(task.course) }))); }, 500); }; rightDiv.appendChild(completeBtn); rightDiv.appendChild(badge); li.appendChild(leftDiv); li.appendChild(rightDiv); taskList.appendChild(li); } }); } function checkFutureTasks(tasks) { const viewEnd = $('#calendar').fullCalendar('getView').end; const completed = getCompletedTasksFromCookies(); const hasFuturePending = tasks.some(task => moment(task.start).isAfter(viewEnd) && !completed[`${task.course}-${task.title}`]); const nextBtn = document.querySelector('.fc-next-button'); if (hasFuturePending) nextBtn.classList.add('alert-next'); else nextBtn.classList.remove('alert-next'); } document.getElementById("toggle-completed").addEventListener("click", () => { const list = document.getElementById("completed-tasks"); list.classList.toggle("d-none"); const title = document.getElementById("toggle-completed"); title.textContent = list.classList.contains("d-none") ? "Completadas ⬇️" : "Completadas ⬆️"; }); window.addEventListener("DOMContentLoaded", () => { const savedTheme = localStorage.getItem("theme"); if (savedTheme === "dark") document.body.classList.add("dark-mode"); }); document.getElementById("toggleTheme").addEventListener("click", () => { const isDark = document.body.classList.toggle("dark-mode"); localStorage.setItem("theme", isDark ? "dark" : "light"); const events = $('#calendar').fullCalendar('clientEvents'); $('#calendar').fullCalendar('removeEvents'); $('#calendar').fullCalendar('addEventSource', events.map(event => ({ ...event, color: getCourseColor(event.course || event.title) }))); }); fetchData();