const API = '/api/pendientes'; let currentDate = new Date(); let todosPendientes = []; const monthNames = ['Enero','Febrero','Marzo','Abril','Mayo','Junio','Julio','Agosto','Septiembre','Octubre','Noviembre','Diciembre']; async function cargarPendientes() { const res = await fetch(API); todosPendientes = await res.json(); renderCalendar(); renderLista(); renderCompletados(); } function formatearFecha(fechaStr) { const partes = fechaStr.split('-'); return partes[2] + '-' + partes[1] + '-' + partes[0]; } function hoyStr() { const hoy = new Date(); return hoy.getFullYear() + '-' + String(hoy.getMonth() + 1).padStart(2, '0') + '-' + String(hoy.getDate()).padStart(2, '0'); } function esVencido(p) { if (p.estado === 'completado') return false; return p.fecha < hoyStr(); } function renderCalendar() { const year = currentDate.getFullYear(); const month = currentDate.getMonth(); document.getElementById('monthYear').textContent = monthNames[month] + ' ' + year; const firstDay = new Date(year, month, 1).getDay(); const daysInMonth = new Date(year, month + 1, 0).getDate(); const grid = document.getElementById('calendarGrid'); grid.innerHTML = ''; for (let i = 0; i < firstDay; i++) { grid.appendChild(document.createElement('div')); } for (let d = 1; d <= daysInMonth; d++) { const dateStr = year + '-' + String(month + 1).padStart(2, '0') + '-' + String(d).padStart(2, '0'); const delDia = todosPendientes.filter(function(p) { return p.fecha === dateStr; }); const dayEl = document.createElement('div'); const tieneVencidos = delDia.some(function(p) { return esVencido(p); }); dayEl.className = 'day' + (tieneVencidos ? ' vencido-dia' : ''); let html = '' + d + ''; if (delDia.length) { const vencidosCount = delDia.filter(function(p) { return esVencido(p); }).length; const badgeClass = vencidosCount > 0 ? 'day-count vencidos' : 'day-count'; const badgeText = delDia.length > 9 ? '9+' : String(delDia.length); html += '' + badgeText + ''; const primero = delDia[0]; const texto = escaparHtml(primero.descripcion); html += '
' + texto + '
'; } dayEl.innerHTML = html; dayEl.onclick = function() { abrirModalDia(dateStr, delDia); }; grid.appendChild(dayEl); } } function abrirModalDia(fecha, pendientes) { document.getElementById('modalTitulo').textContent = 'Pendientes del ' + formatearFecha(fecha); const cont = document.getElementById('modalPendientes'); cont.innerHTML = ''; const noCompletados = pendientes.filter(function(p) { return p.estado !== 'completado'; }); const completados = pendientes.filter(function(p) { return p.estado === 'completado'; }); if (!pendientes.length) { cont.innerHTML = '

No hay pendientes para este dia.

'; } else { if (noCompletados.length) { const tit = document.createElement('div'); tit.className = 'subsubtitulo'; tit.textContent = 'Pendientes (' + noCompletados.length + ')'; cont.appendChild(tit); noCompletados.forEach(function(p) { cont.appendChild(crearCardPendiente(p)); }); } if (completados.length) { const titC = document.createElement('div'); titC.className = 'subsubtitulo'; titC.textContent = 'Completados (' + completados.length + ')'; cont.appendChild(titC); completados.forEach(function(p) { cont.appendChild(crearCardPendiente(p)); }); } } document.getElementById('modalDia').classList.remove('hidden'); } function crearCardPendiente(p) { const vencido = esVencido(p); const div = document.createElement('div'); div.className = 'pendiente-card ' + (vencido ? 'vencido' : p.urgencia); let html = '
' + '
' + '
' + escaparHtml(p.descripcion) + ''; if (vencido) { html += 'Vencido'; } html += '
' + '
' + formatearFecha(p.fecha) + ' - ' + p.estado + ' - Urgencia: ' + p.urgencia + '
' + '
' + '' + '
'; html += '
'; html += '
'; html += 'Prioridad:'; ['alta', 'media', 'baja'].forEach(function(u) { const activo = p.urgencia === u ? ' activo' : ''; const etiqueta = u.charAt(0).toUpperCase() + u.slice(1); html += ''; }); html += '
'; html += '
'; html += 'Estado:'; [ { val: 'pendiente', label: 'Pendiente' }, { val: 'en progreso', label: 'En proceso' }, { val: 'completado', label: 'Completado' } ].forEach(function(e) { const activo = p.estado === e.val ? ' activo' : ''; const cls = e.val.replace(' ', '_'); html += ''; }); html += '
'; html += '
'; div.innerHTML = html; return div; } function escaparHtml(texto) { const div = document.createElement('div'); div.textContent = texto; return div.innerHTML; } let menuAbiertoId = null; function toggleMenu(id) { if (menuAbiertoId === id) { const m = document.getElementById('menu-' + menuAbiertoId); if (m) m.classList.remove('show'); menuAbiertoId = null; return; } if (menuAbiertoId !== null) { const prev = document.getElementById('menu-' + menuAbiertoId); if (prev) prev.classList.remove('show'); } const menu = document.getElementById('menu-' + id); if (menu) { menu.classList.add('show'); menuAbiertoId = id; } } document.addEventListener('click', function(e) { if (menuAbiertoId !== null && !e.target.closest('.menu-wrapper')) { const prev = document.getElementById('menu-' + menuAbiertoId); if (prev) prev.classList.remove('show'); menuAbiertoId = null; } }); async function cambioRapido(id, campo, valor) { const p = todosPendientes.find(function(x) { return x.id === id; }); if (!p) return; const body = { descripcion: p.descripcion, fecha: p.fecha, estado: p.estado, urgencia: p.urgencia }; body[campo] = valor; await fetch(API + '/' + id, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body) }); cargarPendientes(); } document.getElementById('cerrarModal').onclick = function() { document.getElementById('modalDia').classList.add('hidden'); }; document.getElementById('prevMonth').onclick = function() { currentDate.setMonth(currentDate.getMonth() - 1); renderCalendar(); }; document.getElementById('nextMonth').onclick = function() { currentDate.setMonth(currentDate.getMonth() + 1); renderCalendar(); }; document.querySelectorAll('.tab').forEach(function(tab) { tab.onclick = function() { document.querySelectorAll('.tab').forEach(function(t) { t.classList.remove('active'); }); document.querySelectorAll('.section').forEach(function(s) { s.classList.remove('active'); }); tab.classList.add('active'); document.getElementById(tab.dataset.tab).classList.add('active'); }; }); document.getElementById('formPendiente').onsubmit = async function(e) { e.preventDefault(); const id = document.getElementById('pendienteId').value; const body = { descripcion: document.getElementById('descripcion').value, fecha: document.getElementById('fecha').value, estado: document.getElementById('estado').value, urgencia: document.getElementById('urgencia').value }; if (id) { await fetch(API + '/' + id, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body) }); } else { await fetch(API, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body) }); } resetForm(); cargarPendientes(); }; document.getElementById('btnCancelar').onclick = resetForm; function resetForm() { document.getElementById('formPendiente').reset(); document.getElementById('pendienteId').value = ''; document.getElementById('btnGuardar').textContent = 'Guardar'; document.getElementById('btnCancelar').classList.add('hidden'); } function renderLista() { const cont = document.getElementById('listaPendientes'); cont.innerHTML = ''; const noCompletados = todosPendientes.filter(function(p) { return p.estado !== 'completado'; }); const vencidos = noCompletados.filter(function(p) { return esVencido(p); }); const normales = noCompletados.filter(function(p) { return !esVencido(p); }); if (!noCompletados.length) { cont.innerHTML = '

No hay pendientes.

'; return; } const titTotal = document.createElement('div'); titTotal.className = 'subtitulo'; titTotal.textContent = 'Pendientes (' + noCompletados.length + ')'; cont.appendChild(titTotal); if (vencidos.length) { const titVencidos = document.createElement('div'); titVencidos.className = 'subsubtitulo'; titVencidos.textContent = 'Vencidos / Prioritarios (' + vencidos.length + ')'; cont.appendChild(titVencidos); vencidos.forEach(function(p) { cont.appendChild(crearCardPendiente(p)); }); } if (normales.length) { const titNormales = document.createElement('div'); titNormales.className = 'subsubtitulo'; titNormales.textContent = 'Pendientes (' + normales.length + ')'; cont.appendChild(titNormales); normales.forEach(function(p) { cont.appendChild(crearCardPendiente(p)); }); } } function renderCompletados() { const cont = document.getElementById('listaCompletados'); if (!cont) return; cont.innerHTML = ''; const completados = todosPendientes.filter(function(p) { return p.estado === 'completado'; }); if (!completados.length) { cont.innerHTML = '

No hay completados aun.

'; return; } const tit = document.createElement('div'); tit.className = 'subtitulo'; tit.textContent = 'Completados (' + completados.length + ')'; cont.appendChild(tit); completados.forEach(function(p) { cont.appendChild(crearCardPendiente(p)); }); } async function editarPendiente(id) { const p = todosPendientes.find(function(x) { return x.id === id; }); if (!p) return; document.getElementById('pendienteId').value = p.id; document.getElementById('descripcion').value = p.descripcion; document.getElementById('fecha').value = p.fecha; document.getElementById('estado').value = p.estado; document.getElementById('urgencia').value = p.urgencia; document.getElementById('btnGuardar').textContent = 'Actualizar'; document.getElementById('btnCancelar').classList.remove('hidden'); document.querySelector('.tab[data-tab=pendientes]').click(); } async function eliminarPendiente(id) { if (!confirm('Eliminar este pendiente?')) return; await fetch(API + '/' + id, { method: 'DELETE' }); cargarPendientes(); } cargarPendientes();