Actualizacion de temario y audios

This commit is contained in:
Tatiana Villa 2026-05-14 12:27:16 +02:00
parent 4fbcee2f66
commit ff9a71178a
3 changed files with 312 additions and 1 deletions

View File

@ -75,6 +75,119 @@ h1 { font-size: 18pt; text-align: center; margin-bottom: 0.2em; color: var(--acc
.leyenda-item { display: flex; align-items: center; gap: 5px; } .leyenda-item { display: flex; align-items: center; gap: 5px; }
.leyenda-color { width: 14px; height: 14px; border: 1px solid var(--border); flex-shrink: 0; } .leyenda-color { width: 14px; height: 14px; border: 1px solid var(--border); flex-shrink: 0; }
/* ── Hoy ────────────────────────────────────────────────── */
.dia.hoy {
border-color: var(--accent-2) !important;
box-shadow: 0 0 0 2px rgba(78,201,176,.25);
}
.dia.hoy > .num { color: var(--accent-2) !important; }
.hoy-label {
display: inline-block;
font-size: 7pt; font-weight: bold; letter-spacing: .06em;
color: var(--accent-2);
background: rgba(78,201,176,.12);
border: 1px solid var(--accent-2);
border-radius: 3px;
padding: 0 4px;
margin-left: 4px; margin-bottom: 4px;
vertical-align: middle;
}
/* ── Filas de tema ──────────────────────────────────────── */
.tema-row {
display: flex;
align-items: flex-start;
gap: 2px;
margin-bottom: 2px;
}
.tema-row .tema-btn { flex: 1; margin-bottom: 0; }
/* ── Botón check ────────────────────────────────────────── */
.check-btn {
flex-shrink: 0;
width: 15px; height: 15px;
border: 1px solid var(--border);
background: var(--bg);
color: var(--text-muted);
cursor: pointer;
font-size: 8pt; line-height: 1;
border-radius: 3px;
display: flex; align-items: center; justify-content: center;
padding: 0; margin-top: 1px;
transition: background .1s, color .1s, border-color .1s;
}
.check-btn:hover { border-color: var(--accent-2); color: var(--accent-2); }
.check-btn.checked { background: var(--accent-2); color: #111; border-color: var(--accent-2); }
/* ── Botón mover ────────────────────────────────────────── */
.move-btn {
flex-shrink: 0;
width: 14px; height: 14px;
border: none; background: transparent;
color: var(--text-muted); cursor: pointer;
font-size: 9pt; line-height: 1;
padding: 0; margin-top: 1px;
opacity: 0; transition: opacity .15s;
}
.tema-row:hover .move-btn { opacity: 1; }
.move-btn:hover { color: var(--warning); }
/* ── Completado ─────────────────────────────────────────── */
.tema-btn.completado {
text-decoration: line-through;
color: var(--text-muted) !important;
opacity: .45;
}
/* ── Atrasado ───────────────────────────────────────────── */
.tema-row.atrasado .tema-btn { color: var(--error) !important; }
.tema-row.atrasado .tema-btn::before { content: '⚠ '; }
.overdue-badge {
display: inline-block;
font-size: 7pt; font-weight: bold;
background: var(--error); color: #fff;
padding: 1px 6px; border-radius: 8px;
margin-bottom: 4px; margin-top: 1px;
-webkit-print-color-adjust: exact; print-color-adjust: exact;
}
/* ── Picker de día ──────────────────────────────────────── */
.move-picker {
position: absolute; right: 0; top: 100%;
z-index: 200;
background: var(--bg-alt);
border: 1px solid var(--border);
border-radius: 4px;
padding: 5px;
display: flex; flex-direction: column; gap: 2px;
min-width: 130px;
box-shadow: 0 4px 14px rgba(0,0,0,.55);
}
.mp-label {
font-size: 8pt; color: var(--text-muted);
padding: 0 3px 3px; border-bottom: 1px solid var(--border);
margin-bottom: 2px;
}
.move-picker button {
background: var(--bg); border: 1px solid var(--border);
color: var(--text); font-size: 8pt; padding: 3px 8px;
text-align: left; cursor: pointer; border-radius: 3px;
font-family: inherit;
}
.move-picker button:hover { background: var(--bg-hover); color: var(--accent-2); }
.move-picker .mp-today { border-color: var(--accent-2); color: var(--accent-2); }
.move-picker .mp-reset { color: var(--text-muted); font-size: 7.5pt; margin-top: 2px; }
.move-picker .mp-reset:hover { color: var(--error); }
/* ── Botón reset planning ───────────────────────────────── */
.btn-reset-planning {
background: none; border: none; cursor: pointer;
color: var(--text-muted); font-size: 10pt;
padding: 0 4px; vertical-align: middle; line-height: 1;
margin-left: 6px; transition: color .15s;
}
.btn-reset-planning:hover { color: var(--warning); }
@media print { @media print {
body { background: #fff; color: #111; padding: 0.8cm 1cm; } body { background: #fff; color: #111; padding: 0.8cm 1cm; }
.dia { background: #fff !important; border-color: #ccc !important; } .dia { background: #fff !important; border-color: #ccc !important; }
@ -84,4 +197,7 @@ h1 { font-size: 18pt; text-align: center; margin-bottom: 0.2em; color: var(--acc
.tema-btn { color: #111; } .tema-btn { color: #111; }
.semana { page-break-after: always; break-after: page; } .semana { page-break-after: always; break-after: page; }
.semana:last-of-type { page-break-after: auto; } .semana:last-of-type { page-break-after: auto; }
.check-btn, .move-btn, .move-picker, .overdue-badge,
.hoy-label, .btn-reset-planning { display: none !important; }
.tema-btn.completado { text-decoration: none; opacity: 1; color: inherit !important; }
} }

View File

@ -0,0 +1,192 @@
'use strict';
(function () {
/* ── Configuración ─────────────────────────────────────── */
const YEAR = 2026;
const MONTH = 4; // Mayo (0-based)
const USERNAME = document.querySelector('.user-email')?.textContent.trim() || 'guest';
const KEY = `planning_${YEAR}_${USERNAME}`;
/* ── Persistencia ──────────────────────────────────────── */
function load() {
try {
const s = JSON.parse(localStorage.getItem(KEY));
return { completed: s?.completed || [], moved: s?.moved || {} };
} catch { return { completed: [], moved: {} }; }
}
function save(s) { localStorage.setItem(KEY, JSON.stringify(s)); }
/* ── Fecha actual ──────────────────────────────────────── */
const now = new Date();
const todayDay = (now.getFullYear() === YEAR && now.getMonth() === MONTH)
? now.getDate() : null;
/* ── Mapa de días ──────────────────────────────────────── */
// dayMap: { 8: diaDom, 9: diaDom, … }
const dayMap = {};
document.querySelectorAll('.dia').forEach(dia => {
const numEl = dia.querySelector(':scope > .num');
if (!numEl) return;
const n = parseInt(numEl.textContent.trim(), 10);
if (!isNaN(n)) dayMap[n] = dia;
});
/* ── Marcar hoy ────────────────────────────────────────── */
if (todayDay && dayMap[todayDay]) {
const d = dayMap[todayDay];
d.classList.add('hoy');
const numEl = d.querySelector(':scope > .num');
if (numEl) numEl.insertAdjacentHTML('afterend', '<span class="hoy-label">HOY</span>');
}
/* ── Envolver .tema-btn en .tema-row ───────────────────── */
document.querySelectorAll('.tema-btn').forEach(btn => {
const row = document.createElement('div');
row.className = 'tema-row';
btn.before(row);
row.appendChild(btn);
});
/* ── Aplicar temas movidos al cargar ───────────────────── */
const state = load();
Object.entries(state.moved).forEach(([href, dayNum]) => {
const btn = [...document.querySelectorAll('.tema-btn')]
.find(b => b.getAttribute('href') === href);
const row = btn?.closest('.tema-row');
const contenido = dayMap[Number(dayNum)]?.querySelector('.contenido');
if (row && contenido && !contenido.contains(row)) {
contenido.appendChild(row);
}
});
/* ── Añadir controles a cada fila ──────────────────────── */
const completedSet = new Set(state.completed);
document.querySelectorAll('.tema-row').forEach(row => {
const btn = row.querySelector('.tema-btn');
if (!btn) return;
const id = btn.getAttribute('href');
const done = completedSet.has(id);
// ── Botón ✓ / ○ ──
const chk = document.createElement('button');
chk.className = 'check-btn' + (done ? ' checked' : '');
chk.title = done ? 'Marcar como pendiente' : 'Marcar como hecho';
chk.textContent = done ? '✓' : '○';
row.insertBefore(chk, btn);
if (done) btn.classList.add('completado');
chk.addEventListener('click', e => {
e.preventDefault();
const s = load();
const set = new Set(s.completed);
const nowDone = !set.has(id);
nowDone ? set.add(id) : set.delete(id);
s.completed = [...set];
save(s);
chk.classList.toggle('checked', nowDone);
chk.textContent = nowDone ? '✓' : '○';
btn.classList.toggle('completado', nowDone);
updateOverdue();
});
// ── Botón mover ──
const mv = document.createElement('button');
mv.className = 'move-btn';
mv.title = 'Mover a otro día';
mv.textContent = '⇌';
row.appendChild(mv);
mv.addEventListener('click', e => { e.preventDefault(); showPicker(row, id); });
});
/* ── Picker de día ─────────────────────────────────────── */
function showPicker(row, id) {
document.querySelectorAll('.move-picker').forEach(p => p.remove());
const picker = document.createElement('div');
picker.className = 'move-picker';
picker.insertAdjacentHTML('afterbegin', '<div class="mp-label">Mover a:</div>');
const days = Object.keys(dayMap).map(Number)
.filter(d => !dayMap[d].classList.contains('vacio')
&& !dayMap[d].classList.contains('examen'))
.sort((a, b) => a - b);
days.forEach(d => {
const b = document.createElement('button');
b.textContent = `Día ${d}` + (d === todayDay ? ' · hoy' : '');
if (d === todayDay) b.classList.add('mp-today');
b.addEventListener('click', e => {
e.stopPropagation();
const s = load();
s.moved[id] = d;
save(s);
const c = dayMap[d]?.querySelector('.contenido');
if (c && !c.contains(row)) c.appendChild(row);
picker.remove();
updateOverdue();
});
picker.appendChild(b);
});
const rst = document.createElement('button');
rst.className = 'mp-reset';
rst.textContent = '↺ Restaurar original';
rst.addEventListener('click', e => {
e.stopPropagation();
const s = load();
delete s.moved[id];
save(s);
location.reload();
});
picker.appendChild(rst);
row.style.position = 'relative';
row.appendChild(picker);
setTimeout(() => {
document.addEventListener('click', function h(e) {
if (!picker.contains(e.target)) {
picker.remove();
document.removeEventListener('click', h);
}
});
}, 0);
}
/* ── Indicadores de atrasados ──────────────────────────── */
function updateOverdue() {
const s = load();
const done = new Set(s.completed);
Object.entries(dayMap).forEach(([dayStr, dia]) => {
const d = Number(dayStr);
dia.querySelectorAll('.tema-row').forEach(row => {
const id = row.querySelector('.tema-btn')?.getAttribute('href');
const overdue = !!id && todayDay != null && d < todayDay && !done.has(id);
row.classList.toggle('atrasado', overdue);
});
const n = dia.querySelectorAll('.tema-row.atrasado').length;
let badge = dia.querySelector('.overdue-badge');
if (n > 0) {
if (!badge) {
badge = document.createElement('span');
badge.className = 'overdue-badge';
dia.querySelector(':scope > .num')?.insertAdjacentElement('afterend', badge);
}
badge.textContent = `${n} atrasado${n > 1 ? 's' : ''}`;
} else if (badge) {
badge.remove();
}
});
}
updateOverdue();
/* ── Reiniciar progreso ────────────────────────────────── */
document.getElementById('btn-reset-planning')?.addEventListener('click', () => {
if (confirm('¿Reiniciar todo el progreso guardado?')) {
localStorage.removeItem(KEY);
location.reload();
}
});
})();

View File

@ -33,7 +33,9 @@
</nav> </nav>
<h1>Planning de repaso TAI</h1> <h1>Planning de repaso TAI</h1>
<p class="subtitle">Mayo 2026 · Examen: <strong>sábado 23 de mayo</strong></p> <p class="subtitle">Mayo 2026 · Examen: <strong>sábado 23 de mayo</strong>
<button id="btn-reset-planning" class="btn-reset-planning" title="Reiniciar progreso guardado"></button>
</p>
<!-- SEMANA 1: 511 mayo --> <!-- SEMANA 1: 511 mayo -->
<section class="semana"> <section class="semana">
@ -179,5 +181,6 @@
<div class="leyenda-item"><div class="leyenda-color" style="background:#1a2a35;-webkit-print-color-adjust:exact;print-color-adjust:exact;"></div> Fin de semana</div> <div class="leyenda-item"><div class="leyenda-color" style="background:#1a2a35;-webkit-print-color-adjust:exact;print-color-adjust:exact;"></div> Fin de semana</div>
</div> </div>
<script th:src="@{/js/planning.js}" defer></script>
</body> </body>
</html> </html>