Actualizacion de temario y audios
This commit is contained in:
parent
4fbcee2f66
commit
ff9a71178a
|
|
@ -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-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 {
|
||||
body { background: #fff; color: #111; padding: 0.8cm 1cm; }
|
||||
.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; }
|
||||
.semana { page-break-after: always; break-after: page; }
|
||||
.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; }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
});
|
||||
|
||||
})();
|
||||
|
|
@ -33,7 +33,9 @@
|
|||
</nav>
|
||||
|
||||
<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: 5–11 mayo -->
|
||||
<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>
|
||||
|
||||
<script th:src="@{/js/planning.js}" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
Loading…
Reference in New Issue