recursos-catolicos/frontend/js/diario.js

310 lines
12 KiB
JavaScript

// ================================
// DIARIO DE ORACIÓN
// Persistencia en API (con fallback a localStorage si no hay conexión).
// Requiere: api-config.js, auth.js
// ================================
const ESTADOS_DIARIO = {
paz: { icono: '🕊️', label: 'Paz' },
gratitud: { icono: '🙏', label: 'Gratitud' },
lucha: { icono: '😔', label: 'Lucha' },
gozo: { icono: '✨', label: 'Gozo' },
silencio: { icono: '🌿', label: 'Silencio' }
};
let _usuario = null;
let _fechaSeleccionada = null;
// Cache en memoria para evitar peticiones redundantes durante la sesión.
const _cache = {};
// ── INICIALIZACIÓN ──────────────────────────────────────────
document.addEventListener("DOMContentLoaded", () => {
_usuario = verificarAuth();
if (!_usuario) return;
const hoy = new Date();
_fechaSeleccionada = toFechaISO(hoy);
document.getElementById('saludo-usuario').textContent = saludoPersonal(_usuario.nombre, hoy);
document.getElementById('fecha-entrada').value = _fechaSeleccionada;
cargarEntrada(_fechaSeleccionada);
cargarListaEntradas();
document.getElementById('btn-guardar-entrada').addEventListener('click', guardarEntrada);
document.getElementById('btn-borrar-entrada').addEventListener('click', borrarEntrada);
document.getElementById('btn-anterior').addEventListener('click', () => navegarFecha(-1));
document.getElementById('btn-siguiente').addEventListener('click', () => navegarFecha(1));
document.getElementById('fecha-entrada').addEventListener('change', e => {
_fechaSeleccionada = e.target.value;
cargarEntrada(_fechaSeleccionada);
});
document.querySelectorAll('.btn-estado').forEach(btn => {
btn.addEventListener('click', () => seleccionarEstado(btn.dataset.estado));
});
});
// ── SALUDO ──────────────────────────────────────────────────
function saludoPersonal(nombre, fecha) {
const h = fecha.getHours();
const franja = h < 13 ? 'Buenos días' : h < 20 ? 'Buenas tardes' : 'Buenas noches';
return `${franja}, ${nombre}. Un momento de silencio contigo.`;
}
// ── API ─────────────────────────────────────────────────────
async function _apiGetEntrada(fecha) {
try {
const res = await apiCall(`/diario/${fecha}`);
if (!res) return null;
if (res.status === 404) return null;
if (!res.ok) throw new Error('Error al cargar entrada');
return await res.json();
} catch (e) {
return _localGetEntrada(fecha);
}
}
async function _apiGetTodas() {
try {
const res = await apiCall('/diario');
if (!res || !res.ok) throw new Error('Error al cargar entradas');
return await res.json();
} catch (e) {
return _localGetTodas();
}
}
async function _apiGuardar(fecha, titulo, texto, estado) {
try {
const res = await apiCall('/diario', {
method: 'POST',
body: JSON.stringify({ fecha, titulo, texto, estado })
});
if (!res || !res.ok) throw new Error('Error al guardar');
const dto = await res.json();
_localSetEntrada(fecha, { id: dto.id, fecha, titulo, texto, estado });
return dto;
} catch (e) {
_localSetEntrada(fecha, { fecha, titulo, texto, estado });
return { fecha, titulo, texto, estado };
}
}
async function _apiEliminar(fecha) {
const entrada = _cache[fecha] || _localGetEntrada(fecha);
if (entrada?.id) {
try {
await apiCall(`/diario/${entrada.id}`, { method: 'DELETE' });
} catch (e) { /* sin conexión: eliminar solo local */ }
}
_localEliminarEntrada(fecha);
delete _cache[fecha];
}
// ── LOCALSTORAGE (fallback / caché offline) ─────────────────
function _lsKey() { return `diario_${_usuario.id}`; }
function _localGetTodas() {
try {
const data = localStorage.getItem(_lsKey());
const obj = data ? JSON.parse(data) : {};
return Object.values(obj);
} catch (e) { return []; }
}
function _localGetEntrada(fecha) {
try {
const data = localStorage.getItem(_lsKey());
const obj = data ? JSON.parse(data) : {};
return obj[fecha] || null;
} catch (e) { return null; }
}
function _localSetEntrada(fecha, entrada) {
try {
const data = localStorage.getItem(_lsKey());
const obj = data ? JSON.parse(data) : {};
obj[fecha] = entrada;
localStorage.setItem(_lsKey(), JSON.stringify(obj));
} catch (e) { /* sin espacio */ }
}
function _localEliminarEntrada(fecha) {
try {
const data = localStorage.getItem(_lsKey());
const obj = data ? JSON.parse(data) : {};
delete obj[fecha];
localStorage.setItem(_lsKey(), JSON.stringify(obj));
} catch (e) { /* noop */ }
}
// ── CARGAR ENTRADA ──────────────────────────────────────────
async function cargarEntrada(fecha) {
actualizarFechaDisplay(fecha);
document.getElementById('titulo-entrada').value = '';
document.getElementById('texto-entrada').value = '';
document.querySelectorAll('.btn-estado').forEach(b => b.classList.remove('activo'));
document.getElementById('btn-borrar-entrada').style.display = 'none';
const entrada = await _apiGetEntrada(fecha);
_cache[fecha] = entrada;
if (entrada) {
document.getElementById('titulo-entrada').value = entrada.titulo || '';
document.getElementById('texto-entrada').value = entrada.texto || '';
if (entrada.estado) {
const btn = document.querySelector(`.btn-estado[data-estado="${entrada.estado}"]`);
if (btn) btn.classList.add('activo');
}
document.getElementById('btn-borrar-entrada').style.display = 'inline-block';
}
}
// ── GUARDAR ─────────────────────────────────────────────────
async function guardarEntrada() {
const titulo = document.getElementById('titulo-entrada').value.trim();
const texto = document.getElementById('texto-entrada').value.trim();
const estado = getEstadoSeleccionado();
const btn = document.getElementById('btn-guardar-entrada');
if (!texto) {
mostrarMensajeDiario('Escribe algo antes de guardar 🙏', 'error');
return;
}
btn.disabled = true;
btn.textContent = 'Guardando…';
const dto = await _apiGuardar(_fechaSeleccionada, titulo, texto, estado);
_cache[_fechaSeleccionada] = dto;
btn.disabled = false;
btn.textContent = 'Guardar ✝';
document.getElementById('btn-borrar-entrada').style.display = 'inline-block';
cargarListaEntradas();
mostrarMensajeDiario('Entrada guardada ✝', 'success');
}
// ── BORRAR ───────────────────────────────────────────────────
async function borrarEntrada() {
if (!confirm('¿Eliminar esta entrada del diario?')) return;
await _apiEliminar(_fechaSeleccionada);
document.getElementById('titulo-entrada').value = '';
document.getElementById('texto-entrada').value = '';
document.querySelectorAll('.btn-estado').forEach(b => b.classList.remove('activo'));
document.getElementById('btn-borrar-entrada').style.display = 'none';
cargarListaEntradas();
mostrarMensajeDiario('Entrada eliminada', 'info');
}
// ── ESTADOS DE ÁNIMO ────────────────────────────────────────
function seleccionarEstado(estado) {
const btn = document.querySelector(`.btn-estado[data-estado="${estado}"]`);
if (!btn) return;
const yaActivo = btn.classList.contains('activo');
document.querySelectorAll('.btn-estado').forEach(b => b.classList.remove('activo'));
if (!yaActivo) btn.classList.add('activo');
}
function getEstadoSeleccionado() {
const activo = document.querySelector('.btn-estado.activo');
return activo ? activo.dataset.estado : null;
}
// ── NAVEGACIÓN ───────────────────────────────────────────────
function navegarFecha(delta) {
const d = new Date(_fechaSeleccionada + 'T12:00:00');
d.setDate(d.getDate() + delta);
_fechaSeleccionada = toFechaISO(d);
document.getElementById('fecha-entrada').value = _fechaSeleccionada;
cargarEntrada(_fechaSeleccionada);
}
function actualizarFechaDisplay(fecha) {
const d = new Date(fecha + 'T12:00:00');
const opts = { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' };
document.getElementById('display-fecha').textContent = d.toLocaleDateString('es-ES', opts);
const hoy = toFechaISO(new Date());
document.getElementById('btn-siguiente').disabled = fecha >= hoy;
}
// ── LISTA DE ENTRADAS ───────────────────────────────────────
async function cargarListaEntradas() {
const lista = document.getElementById('lista-entradas');
const sinElem = document.getElementById('sin-entradas');
const entradas = await _apiGetTodas();
const lista_sorted = entradas
.filter(e => e && e.fecha)
.sort((a, b) => b.fecha.localeCompare(a.fecha));
if (lista_sorted.length === 0) {
lista.innerHTML = '';
sinElem.style.display = 'block';
return;
}
sinElem.style.display = 'none';
const hoy = toFechaISO(new Date());
lista.innerHTML = lista_sorted.map(e => {
const fecha = e.fecha;
const d = new Date(fecha + 'T12:00:00');
const fechaStr = d.toLocaleDateString('es-ES', { weekday: 'short', day: 'numeric', month: 'long' });
const icono = ESTADOS_DIARIO[e.estado]?.icono || '🕯';
const preview = (e.texto || '').substring(0, 80) + ((e.texto || '').length > 80 ? '…' : '');
const esHoy = fecha === hoy;
return `
<li class="entrada-item ${esHoy ? 'entrada-hoy' : ''}" onclick="seleccionarFechaLista('${fecha}')">
<span class="entrada-icono">${icono}</span>
<div class="entrada-info">
<span class="entrada-fecha">${fechaStr}${esHoy ? ' <em>(hoy)</em>' : ''}</span>
${e.titulo ? `<span class="entrada-titulo">${e.titulo}</span>` : ''}
<span class="entrada-preview">${preview}</span>
</div>
</li>
`;
}).join('');
}
function seleccionarFechaLista(fecha) {
_fechaSeleccionada = fecha;
document.getElementById('fecha-entrada').value = fecha;
cargarEntrada(fecha);
document.getElementById('editor-diario').scrollIntoView({ behavior: 'smooth' });
}
// ── UTILIDADES ───────────────────────────────────────────────
function toFechaISO(date) {
const offset = date.getTimezoneOffset() * 60000;
return new Date(date - offset).toISOString().split('T')[0];
}
function mostrarMensajeDiario(texto, tipo) {
const msg = document.getElementById('mensaje-diario');
msg.textContent = texto;
msg.className = `mensaje-diario ${tipo}`;
msg.style.display = 'block';
setTimeout(() => { msg.style.display = 'none'; }, 3000);
}