// 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();