actualizaciones post examen TAI

This commit is contained in:
Tatiana Villa Ema 2026-06-15 10:09:30 +02:00
parent 58bc4ef47e
commit b127553151
3 changed files with 543 additions and 146 deletions

2
.gitignore vendored
View File

@ -47,3 +47,5 @@ build/
.venv
.fake

View File

@ -201,3 +201,239 @@ h1 { font-size: 18pt; text-align: center; margin-bottom: 0.2em; color: var(--acc
.hoy-label, .btn-reset-planning { display: none !important; }
.tema-btn.completado { text-decoration: none; opacity: 1; color: inherit !important; }
}
/* ── Planning interactivo 20260615 ───────────────────── */
.planning-container {
max-width: 1200px;
margin: 20px auto;
padding: 0 20px;
}
.planning-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 30px;
flex-wrap: wrap;
gap: 15px;
}
.month-nav {
display: flex;
align-items: center;
gap: 15px;
}
.month-title {
font-size: 24px;
font-weight: bold;
min-width: 200px;
text-align: center;
}
.nav-btn {
background: #007bff;
color: white;
border: none;
padding: 8px 15px;
border-radius: 5px;
cursor: pointer;
font-size: 16px;
transition: background 0.3s;
}
.nav-btn:hover {
background: #0056b3;
}
.add-task-form {
display: flex;
gap: 10px;
margin-bottom: 20px;
flex-wrap: wrap;
}
.add-task-form input {
flex: 1;
min-width: 200px;
padding: 10px;
border: 1px solid #ddd;
border-radius: 5px;
font-size: 14px;
}
.add-task-form button {
background: #28a745;
color: white;
border: none;
padding: 10px 20px;
border-radius: 5px;
cursor: pointer;
font-size: 14px;
transition: background 0.3s;
}
.add-task-form button:hover {
background: #218838;
}
.reset-btn {
background: #dc3545;
color: white;
border: none;
padding: 8px 15px;
border-radius: 5px;
cursor: pointer;
font-size: 14px;
transition: background 0.3s;
}
.reset-btn:hover {
background: #c82333;
}
.calendar-grid {
display: grid;
grid-template-columns: repeat(7, 1fr);
gap: 10px;
margin-top: 20px;
}
.day-header {
text-align: center;
font-weight: bold;
padding: 10px;
background: #f0f0f0;
border-radius: 5px;
}
.day-cell {
border: 1px solid #ddd;
border-radius: 5px;
padding: 10px;
min-height: 150px;
background: white;
cursor: move;
transition: all 0.3s;
position: relative;
}
.day-cell.empty {
background: #f9f9f9;
cursor: default;
}
.day-cell.other-month {
background: #f0f0f0;
opacity: 0.5;
cursor: default;
}
.day-cell.today {
background: #e7f3ff;
border: 2px solid #007bff;
}
.day-cell.weekend {
background: #fff8f0;
}
.day-cell:hover:not(.empty):not(.other-month) {
box-shadow: 0 2px 8px rgba(0,0,0,0.15);
transform: translateY(-2px);
}
.day-number {
font-weight: bold;
margin-bottom: 8px;
color: #333;
}
.day-number.weekend {
color: #d9534f;
}
.tasks-list {
display: flex;
flex-direction: column;
gap: 5px;
}
.task-item {
background: #007bff;
color: white;
padding: 6px 8px;
border-radius: 3px;
font-size: 12px;
display: flex;
justify-content: space-between;
align-items: center;
word-break: break-word;
cursor: grab;
transition: all 0.2s;
}
.task-item:active {
cursor: grabbing;
opacity: 0.8;
}
.task-item.dragging {
opacity: 0.5;
}
.task-item:hover {
background: #0056b3;
}
.task-delete-btn {
background: none;
border: none;
color: white;
cursor: pointer;
font-size: 14px;
padding: 0 4px;
margin-left: 4px;
transition: all 0.2s;
}
.task-delete-btn:hover {
color: #ffcccc;
transform: scale(1.2);
}
.drop-zone {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
border-radius: 5px;
display: none;
border: 2px dashed #007bff;
background: rgba(0, 123, 255, 0.1);
}
.day-cell.drag-over .drop-zone {
display: block;
}
@media (max-width: 768px) {
.calendar-grid {
grid-template-columns: repeat(7, 1fr);
gap: 5px;
}
.day-cell {
min-height: 120px;
padding: 8px;
font-size: 12px;
}
.task-item {
font-size: 11px;
padding: 4px 6px;
}
}

View File

@ -3,9 +3,8 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Planning TAI — Mayo 2026</title>
<title>Planning TAI — Agenda</title>
<link rel="stylesheet" th:href="@{/css/style.css}">
<link rel="stylesheet" th:href="@{/css/planning.css}">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.0/css/all.min.css">
</head>
<body>
@ -32,155 +31,315 @@
</div>
</nav>
<h1>Planning de repaso TAI</h1>
<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>
<div class="planning-container">
<h1><i class="fas fa-calendar-alt"></i> Mi Planning 2026</h1>
<!-- SEMANA 1: 511 mayo -->
<section class="semana">
<div class="cabecera-dias">
<span>Lun</span><span>Mar</span><span>Mié</span><span>Jue</span><span>Vie</span><span>Sáb</span><span>Dom</span>
</div>
<div class="dias">
<div class="dia vacio"></div>
<div class="dia vacio"></div>
<div class="dia vacio"></div>
<div class="dia vacio"></div>
<div class="dia">
<span class="num">8</span>
<span class="contenido">Preparar el planning<br>Preparar flashcards</span>
<!-- Header con navegación -->
<div class="planning-header">
<div class="month-nav">
<button class="nav-btn" id="prevMonth" title="Mes anterior"><i class="fas fa-chevron-left"></i></button>
<div class="month-title" id="monthTitle"></div>
<button class="nav-btn" id="nextMonth" title="Próximo mes"><i class="fas fa-chevron-right"></i></button>
</div>
<button class="reset-btn" id="resetBtn" title="Limpiar todo"><i class="fas fa-trash"></i> Limpiar todo</button>
</div>
<div class="dia fin-semana">
<span class="num">9</span>
<span class="contenido">
<a class="tema-btn" href="/curso?bloque=1&tema=1">1.1. La Constitución Española de 1978. Derechos y deberes fundamentales. Su garantía y suspensi…</a>
<a class="tema-btn" href="/curso?bloque=1&tema=2">1.2. Las Cortes Generales: atribuciones del Congreso de los Diputados y del Senado. El Tribunal…</a>
<a class="tema-btn" href="/curso?bloque=1&tema=3">1.3. El Gobierno: composición, nombramiento y cese. Las funciones del Gobierno. Relaciones entr…</a>
<a class="tema-btn" href="/curso?bloque=1&tema=4">1.4. Estatuto Básico del Empleado Público: derechos y deberes, provisión de puestos, promoción …</a>
</span>
</div>
<div class="dia fin-semana">
<span class="num">10</span>
<span class="contenido">
<a class="tema-btn" href="/curso?bloque=1&tema=5">1.5. Políticas de igualdad y contra la violencia de género. Igualdad LGTBI. Discapacidad y depe…</a>
<a class="tema-btn" href="/curso?bloque=1&tema=6">1.6. Sociedad de la información. Identidad y firma electrónica. DNIe. Agenda Digital para Españ…</a>
<a class="tema-btn" href="/curso?bloque=1&tema=7">1.7. Protección de datos personales: principios, derechos y obligaciones. Derechos digitales.</a>
<a class="tema-btn" href="/curso?bloque=1&tema=8">1.8. Acceso electrónico a los servicios públicos. Registros, notificaciones, medios electrónico…</a>
</span>
</div>
</div>
</section>
<!-- SEMANA 2: 1218 mayo -->
<section class="semana">
<div class="cabecera-dias">
<span>Lun</span><span>Mar</span><span>Mié</span><span>Jue</span><span>Vie</span><span>Sáb</span><span>Dom</span>
</div>
<div class="dias">
<div class="dia">
<span class="num">11</span>
<span class="contenido">
<a class="tema-btn" href="/curso?bloque=1&tema=9">1.9. Instrumentos de acceso electrónico: sedes electrónicas, canales, identificación y autentic…</a>
<a class="tema-btn" href="/curso?bloque=2&tema=1">2.1. Informática básica. Representación y comunicación de la información: elementos constitutiv…</a>
<a class="tema-btn" href="/curso?bloque=2&tema=2">2.2. Periféricos: conectividad y administración. Elementos de impresión. Elementos de almacenam…</a>
<a class="tema-btn" href="/curso?bloque=2&tema=3">2.3. Tipos abstractos y Estructuras de datos. Organizaciones de ficheros. Algoritmos. Formatos …</a>
</span>
</div>
<div class="dia">
<span class="num">12</span>
<span class="contenido">
<a class="tema-btn" href="/curso?bloque=2&tema=4">2.4. Sistemas operativos. Características y elementos constitutivos. Sistemas Windows. Sistemas…</a>
<a class="tema-btn" href="/curso?bloque=2&tema=5">2.5. Sistemas de gestión de bases de datos relacionales, orientados a objetos y NoSQL: caracter…</a>
<a class="tema-btn" href="/curso?bloque=3&tema=1">3.1. Modelado de datos, metodologías y reglas. Entidades, atributos y relaciones. Diseño de bas…</a>
<a class="tema-btn" href="/curso?bloque=3&tema=2">3.2. Lenguajes de programación. Representación de tipos de datos. Operadores. Instrucciones con…</a>
</span>
</div>
<div class="dia">
<span class="num">13</span>
<span class="contenido">
<a class="tema-btn" href="/curso?bloque=3&tema=3">3.3. Lenguajes de interrogación de bases de datos. Estándar ANSI SQL. Procedimientos almacenado…</a>
<a class="tema-btn" href="/curso?bloque=3&tema=4">3.4. Diseño y programación orientada a objetos. Elementos y componentes software: objetos, clas…</a>
<a class="tema-btn" href="/curso?bloque=3&tema=5">3.5. Arquitectura Java EE/Jakarta EE y plataforma .NET: componentes, persistencia y seguridad. …</a>
</span>
</div>
<div class="dia">
<span class="num">14</span>
<span class="contenido">
<a class="tema-btn" href="/curso?bloque=3&tema=6">3.6. Arquitectura de sistemas cliente/servidor y multicapas: componentes y operación. Arquitect…</a>
<a class="tema-btn" href="/curso?bloque=3&tema=7">3.7. Aplicaciones web. Desarrollo web front-end y en servidor, multiplataforma y multidispositi…</a>
<a class="tema-btn" href="/curso?bloque=3&tema=8">3.8. Accesibilidad, diseño universal y usabilidad. Acceso y usabilidad de las tecnologías, prod…</a>
<a class="tema-btn" href="/curso?bloque=3&tema=9">3.9. Repositorios: estructura y actualización. Generación de código y documentación. Metodologí…</a>
</span>
</div>
<div class="dia">
<span class="num">15</span>
<span class="contenido">
<a class="tema-btn" href="/curso?bloque=4&tema=1">4.1. Administración del Sistema operativo y software de base. Actualización, mantenimiento y re…</a>
<a class="tema-btn" href="/curso?bloque=4&tema=2">4.2. Administración de bases de datos. Sistemas de almacenamiento y su virtualización. Política…</a>
<a class="tema-btn" href="/curso?bloque=4&tema=3">4.3. Administración de servidores de correo electrónico y sus protocolos. Administración de con…</a>
</span>
</div>
<div class="dia fin-semana">
<span class="num">16</span>
<span class="contenido">
<a class="tema-btn" href="/curso?bloque=4&tema=4">4.4. Administración de redes de área local. Gestión de usuarios. Gestión de dispositivos. Monit…</a>
<a class="tema-btn" href="/curso?bloque=4&tema=5">4.5. Conceptos de seguridad de los sistemas de información. Seguridad física. Seguridad lógica.…</a>
<a class="tema-btn" href="/curso?bloque=4&tema=6">4.6. Comunicaciones. Medios de transmisión. Modos de comunicación. Equipos terminales y equipos…</a>
</span>
</div>
<div class="dia fin-semana">
<span class="num">17</span>
<span class="contenido">
<a class="tema-btn" href="/curso?bloque=4&tema=7">4.7. El modelo TCP/IP y el modelo de referencia de interconexión de sistemas abiertos (OSI) de …</a>
<a class="tema-btn" href="/curso?bloque=4&tema=8">4.8. Internet: arquitectura de red. Origen, evolución y estado actual. Principales servicios. P…</a>
<a class="tema-btn" href="/curso?bloque=4&tema=9">4.9. Seguridad y protección en redes de comunicaciones. Seguridad perimetral. Acceso remoto seg…</a>
<a class="tema-btn" href="/curso?bloque=4&tema=10">4.10. Redes locales. Tipología. Técnicas de transmisión. Métodos de acceso. Dispositivos de inte…</a>
</span>
</div>
</div>
</section>
<!-- Formulario para agregar tareas -->
<form class="add-task-form" id="addTaskForm">
<input
type="text"
id="taskInput"
placeholder="Escribe una tarea..."
maxlength="80"
autocomplete="off"
>
<button type="submit"><i class="fas fa-plus"></i> Agregar</button>
</form>
<!-- SEMANA 3: 1925 mayo -->
<section class="semana">
<div class="cabecera-dias">
<span>Lun</span><span>Mar</span><span>Mié</span><span>Jue</span><span>Vie</span><span>Sáb</span><span>Dom</span>
<!-- Calendario -->
<div class="calendar-grid" id="calendarGrid"></div>
</div>
<div class="dias">
<div class="dia">
<span class="num">18</span>
<span class="contenido"><strong>Repaso</strong><br>B1. Organización del Estado y Administración electrónica<br>B2. Tecnología básica</span>
</div>
<div class="dia">
<span class="num">19</span>
<span class="contenido"><strong>Repaso</strong><br>B3. Desarrollo de sistemas<br>B4. Sistemas y comunicaciones</span>
</div>
<div class="dia simulacro">
<span class="num">20</span>
<span class="contenido">🧪 Simulacro<br>Examen completo<br>con tiempo real</span>
</div>
<div class="dia">
<span class="num">21</span>
<span class="contenido">Repaso de fallos<br>del simulacro</span>
</div>
<div class="dia">
<span class="num">22</span>
<span class="contenido">Solo flashcards<br>Nada nuevo<br>Descansar pronto</span>
</div>
<div class="dia examen">
<span class="num">23</span>
<span class="contenido">🎯 EXAMEN</span>
</div>
<div class="dia vacio"></div>
</div>
</section>
<div class="leyenda">
<div class="leyenda-item"><div class="leyenda-color" style="background:#2a2510;border-color:#d7ba7d;-webkit-print-color-adjust:exact;print-color-adjust:exact;"></div> Simulacro</div>
<div class="leyenda-item"><div class="leyenda-color" style="background:#2a1010;border-color:#f44747;-webkit-print-color-adjust:exact;print-color-adjust:exact;"></div> Examen</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>
<script>
// ════════════════════════════════════════════════════════════════
// Planning Customizable - Calendario con Drag & Drop
// ════════════════════════════════════════════════════════════════
<script th:src="@{/js/planning.js}" defer></script>
class PlanningCalendar {
constructor() {
this.currentDate = new Date();
this.tasks = this.loadTasks();
this.draggedTask = null;
this.init();
}
init() {
this.setupEventListeners();
this.render();
}
setupEventListeners() {
document.getElementById('prevMonth').addEventListener('click', () => this.previousMonth());
document.getElementById('nextMonth').addEventListener('click', () => this.nextMonth());
document.getElementById('addTaskForm').addEventListener('submit', (e) => this.addTask(e));
document.getElementById('resetBtn').addEventListener('click', () => this.resetAll());
}
previousMonth() {
this.currentDate.setMonth(this.currentDate.getMonth() - 1);
this.render();
}
nextMonth() {
this.currentDate.setMonth(this.currentDate.getMonth() + 1);
this.render();
}
render() {
this.renderMonthTitle();
this.renderCalendar();
}
renderMonthTitle() {
const options = { month: 'long', year: 'numeric' };
const title = this.currentDate.toLocaleDateString('es-ES', options);
document.getElementById('monthTitle').textContent = title.charAt(0).toUpperCase() + title.slice(1);
}
renderCalendar() {
const grid = document.getElementById('calendarGrid');
grid.innerHTML = '';
// Encabezados de días
const dayNames = ['Lun', 'Mar', 'Mié', 'Jue', 'Vie', 'Sáb', 'Dom'];
dayNames.forEach(day => {
const header = document.createElement('div');
header.className = 'day-header';
header.textContent = day;
grid.appendChild(header);
});
// Obtener primer y último día del mes
const year = this.currentDate.getFullYear();
const month = this.currentDate.getMonth();
const firstDay = new Date(year, month, 1);
const lastDay = new Date(year, month + 1, 0);
// Ajustar para que lunes sea 0
let dayOfWeek = firstDay.getDay() - 1;
if (dayOfWeek === -1) dayOfWeek = 6;
// Días del mes anterior
const prevMonthLastDay = new Date(year, month, 0).getDate();
for (let i = dayOfWeek - 1; i >= 0; i--) {
const cell = this.createDayCell(prevMonthLastDay - i, 'other-month');
grid.appendChild(cell);
}
// Días del mes actual
for (let day = 1; day <= lastDay.getDate(); day++) {
const date = new Date(year, month, day);
const cell = this.createDayCell(day, '', date);
grid.appendChild(cell);
}
// Días del próximo mes
const remainingCells = grid.children.length - 7; // 7 headers
const totalCells = Math.ceil(remainingCells / 7) * 7;
for (let day = 1; day <= totalCells - remainingCells; day++) {
const cell = this.createDayCell(day, 'other-month');
grid.appendChild(cell);
}
}
createDayCell(day, extraClass, date = null) {
const cell = document.createElement('div');
cell.className = `day-cell ${extraClass}`;
if (extraClass === 'other-month') {
cell.classList.add('empty');
} else {
// Es día del mes actual
const isToday = date && this.isToday(date);
const isWeekend = date && (date.getDay() === 0 || date.getDay() === 6);
if (isToday) cell.classList.add('today');
if (isWeekend) cell.classList.add('weekend');
const dayNumber = document.createElement('div');
dayNumber.className = 'day-number' + (isWeekend ? ' weekend' : '');
dayNumber.textContent = day;
cell.appendChild(dayNumber);
const dateKey = this.getDateKey(date);
const dayTasks = this.tasks[dateKey] || [];
const tasksList = document.createElement('div');
tasksList.className = 'tasks-list';
dayTasks.forEach(task => {
const taskEl = this.createTaskElement(task, dateKey);
tasksList.appendChild(taskEl);
});
cell.appendChild(tasksList);
// Agregar zona de drop
const dropZone = document.createElement('div');
dropZone.className = 'drop-zone';
cell.appendChild(dropZone);
// Event listeners para drag & drop
cell.addEventListener('dragover', (e) => this.handleDragOver(e));
cell.addEventListener('drop', (e) => this.handleDrop(e, dateKey));
cell.addEventListener('dragleave', (e) => this.handleDragLeave(e));
}
return cell;
}
createTaskElement(task, dateKey) {
const taskEl = document.createElement('div');
taskEl.className = 'task-item';
taskEl.draggable = true;
taskEl.textContent = task.text;
const deleteBtn = document.createElement('button');
deleteBtn.type = 'button';
deleteBtn.className = 'task-delete-btn';
deleteBtn.textContent = '✕';
deleteBtn.addEventListener('click', (e) => {
e.stopPropagation();
this.deleteTask(task.id, dateKey);
});
taskEl.appendChild(deleteBtn);
taskEl.addEventListener('dragstart', (e) => this.handleDragStart(e, task.id, dateKey));
taskEl.addEventListener('dragend', (e) => this.handleDragEnd(e));
return taskEl;
}
handleDragStart(e, taskId, fromDateKey) {
this.draggedTask = { taskId, fromDateKey };
e.target.classList.add('dragging');
e.dataTransfer.effectAllowed = 'move';
}
handleDragEnd(e) {
e.target.classList.remove('dragging');
}
handleDragOver(e) {
e.preventDefault();
e.dataTransfer.dropEffect = 'move';
e.target.closest('.day-cell').classList.add('drag-over');
}
handleDragLeave(e) {
e.target.closest('.day-cell').classList.remove('drag-over');
}
handleDrop(e, toDateKey) {
e.preventDefault();
e.target.closest('.day-cell').classList.remove('drag-over');
if (!this.draggedTask) return;
const { taskId, fromDateKey } = this.draggedTask;
this.moveTask(taskId, fromDateKey, toDateKey);
this.draggedTask = null;
}
addTask(e) {
e.preventDefault();
const input = document.getElementById('taskInput');
const text = input.value.trim();
if (!text) return;
const today = this.getDateKey(new Date());
const task = {
id: Date.now(),
text: text,
date: today
};
if (!this.tasks[today]) {
this.tasks[today] = [];
}
this.tasks[today].push(task);
this.saveTasks();
input.value = '';
this.render();
}
deleteTask(taskId, dateKey) {
if (this.tasks[dateKey]) {
this.tasks[dateKey] = this.tasks[dateKey].filter(t => t.id !== taskId);
if (this.tasks[dateKey].length === 0) {
delete this.tasks[dateKey];
}
this.saveTasks();
this.render();
}
}
moveTask(taskId, fromDateKey, toDateKey) {
if (!this.tasks[fromDateKey]) return;
const taskIndex = this.tasks[fromDateKey].findIndex(t => t.id === taskId);
if (taskIndex === -1) return;
const task = this.tasks[fromDateKey][taskIndex];
this.tasks[fromDateKey].splice(taskIndex, 1);
if (this.tasks[fromDateKey].length === 0) {
delete this.tasks[fromDateKey];
}
if (!this.tasks[toDateKey]) {
this.tasks[toDateKey] = [];
}
this.tasks[toDateKey].push(task);
this.saveTasks();
this.render();
}
getDateKey(date) {
return date.toISOString().split('T')[0];
}
isToday(date) {
const today = new Date();
return date.toDateString() === today.toDateString();
}
saveTasks() {
localStorage.setItem('planningTasks', JSON.stringify(this.tasks));
}
loadTasks() {
const stored = localStorage.getItem('planningTasks');
return stored ? JSON.parse(stored) : {};
}
resetAll() {
if (confirm('¿Estás seguro de que deseas limpiar todas las tareas?')) {
this.tasks = {};
this.saveTasks();
this.render();
}
}
}
// Inicializar cuando el DOM esté listo
document.addEventListener('DOMContentLoaded', () => {
new PlanningCalendar();
});
</script>
</body>
</html>