first commit
This commit is contained in:
commit
8497b469b7
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,158 @@
|
||||||
|
/* --- VARIABLES DE COLOR (Estilo VS Code Dark) --- */
|
||||||
|
:root {
|
||||||
|
--bg-color: #1e1e1e;
|
||||||
|
--text-color: #d4d4d4;
|
||||||
|
--accent-color: #007acc;
|
||||||
|
--titulos-color: #4ec9b0;
|
||||||
|
--border-color: #3b3b3b;
|
||||||
|
--card-bg: #252526;
|
||||||
|
--hover-bg: #2a2d2e;
|
||||||
|
--success: #6a9955;
|
||||||
|
--error: #f44747;
|
||||||
|
--warning: #d7ba7d;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- ESTILOS GENERALES --- */
|
||||||
|
body {
|
||||||
|
font-family: 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
|
||||||
|
font-size: 1.1rem;
|
||||||
|
line-height: 1.6;
|
||||||
|
color: var(--text-color);
|
||||||
|
background-color: var(--bg-color);
|
||||||
|
margin: 2rem auto;
|
||||||
|
max-width: 850px;
|
||||||
|
padding: 0 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
color: var(--titulos-color);
|
||||||
|
text-align: center;
|
||||||
|
border-bottom: 2px solid var(--accent-color);
|
||||||
|
padding-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- SELECTOR Y BOTONES --- */
|
||||||
|
select, button {
|
||||||
|
background-color: var(--card-bg);
|
||||||
|
color: var(--text-color);
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
padding: 10px 15px;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 1rem;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
select:focus, button:hover {
|
||||||
|
border-color: var(--accent-color);
|
||||||
|
background-color: var(--hover-bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
button#aleatoria {
|
||||||
|
background-color: var(--accent-color);
|
||||||
|
color: white;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- TARJETA DE PREGUNTA --- */
|
||||||
|
.pregunta {
|
||||||
|
background-color: var(--card-bg);
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
padding: 2rem;
|
||||||
|
margin-top: 2rem;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 4px 15px rgba(0,0,0,0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.pregunta p strong {
|
||||||
|
color: var(--titulos-color);
|
||||||
|
font-size: 1.2rem;
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- OPCIONES (Efecto lista de terminal) --- */
|
||||||
|
.opcion {
|
||||||
|
display: block;
|
||||||
|
padding: 12px 15px;
|
||||||
|
margin: 8px 0;
|
||||||
|
background: var(--bg-color);
|
||||||
|
border: 1px solid transparent;
|
||||||
|
border-radius: 5px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.opcion:hover {
|
||||||
|
border-color: var(--accent-color);
|
||||||
|
background: var(--hover-bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.opcion input[type="radio"] {
|
||||||
|
margin-right: 12px;
|
||||||
|
accent-color: var(--accent-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- FEEDBACK Y RESULTADOS --- */
|
||||||
|
#resultado {
|
||||||
|
margin: 20px 0;
|
||||||
|
padding: 15px;
|
||||||
|
border-radius: 5px;
|
||||||
|
border: 2px solid var(--border-color);
|
||||||
|
background: var(--bg-color);
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#feedback {
|
||||||
|
margin-top: 1.5rem;
|
||||||
|
padding: 10px;
|
||||||
|
border-radius: 4px;
|
||||||
|
text-align: center;
|
||||||
|
font-family: 'Consolas', monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- ANIMACIÓN DE CARGA --- */
|
||||||
|
@keyframes fadeIn {
|
||||||
|
from { opacity: 0; transform: translateY(10px); }
|
||||||
|
to { opacity: 1; transform: translateY(0); }
|
||||||
|
}
|
||||||
|
|
||||||
|
.pregunta {
|
||||||
|
animation: fadeIn 0.3s ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.marcador-container {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
background-color: var(--card-bg);
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
border-left: 4px solid var(--accent-color);
|
||||||
|
padding: 15px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-family: 'Consolas', monospace; /* Fuente de código */
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat {
|
||||||
|
font-size: 0.9rem;
|
||||||
|
margin-right: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.verde { color: var(--success); font-weight: bold; }
|
||||||
|
.rojo { color: var(--error); font-weight: bold; }
|
||||||
|
|
||||||
|
.nota-actual {
|
||||||
|
font-size: 1.2rem;
|
||||||
|
color: var(--titulos-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.nota-actual strong {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
border: 1px solid var(--titulos-color);
|
||||||
|
padding: 2px 8px;
|
||||||
|
border-radius: 4px;
|
||||||
|
margin-left: 5px;
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Binary file not shown.
|
After Width: | Height: | Size: 36 KiB |
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,220 @@
|
||||||
|
## SUPUESTO 2
|
||||||
|
El Ministerio de Educación, Formación Profesional y Deportes tiene una red asignada en el Plan de Direccionamiento de la AGE
|
||||||
|
10.9.0.0/16 y ha creado un organismo para la enseñanza online de formadores, OEOF (en adelante nos referiremos en este supuesto sólo como “organismo”), al que le ha correspondido la última de las subredes de las 16 en las que se ha dividido la red ministerial.
|
||||||
|
|
||||||
|
El sistema de enseñanza online de formadores se fundamenta en un servidor de formación accesible también desde Internet en la URL https://www.profesores.es. Los servidores de este sistema (web, http, DNS, DHCP, LDAP), se van a instalar con sistema operativo Linux Ubuntu. El portal web está montado usando Apache Tomcat.
|
||||||
|
|
||||||
|
El certificado del portal (SSL) estará asociado al dominio profesores.es. Para poder gestionar el correo de los profesores, se ha instalado un servidor de correo con tecnología Postfix.
|
||||||
|
|
||||||
|
Los DNS se han instalado usando el software BIND así como la asignación dinámica de direcciones IP mediante DHCP.
|
||||||
|
|
||||||
|
Hay un servidor llamado BIBLIOTECA que contiene un compendio de documentos e información de utilidad para los profesores. El
|
||||||
|
acceso está permitido por RDP. En los elementos de configuración está permitido el acceso remoto para todos los usuarios que en el Directorio Activo estén en el Grupo de Profesores. El resto no podrán conectarse. No hay elementos de red que impidan la conexión libre por el puerto RDP 389 a dicho servidor, desde su misma VLAN.
|
||||||
|
|
||||||
|
La base de datos de la biblioteca es MySQL.
|
||||||
|
|
||||||
|
Además, sobre servidores Ubuntu, se han instalado herramientas de detección de vulnerabilidades (Tenable Nessus y Nmap) y de
|
||||||
|
monitorización de sistemas y de aplicaciones web (Nagios).
|
||||||
|
|
||||||
|
Los usuarios de la red (profesores) usan escritorios virtuales.
|
||||||
|
|
||||||
|
Los switches para configurar las VLANs son CISCO (usan sistema operativo Cisco IOS).
|
||||||
|
|
||||||
|
El CPD principal del organismo tiene un respaldo en un CPD secundario, pero no está configurado como activo-activo y los cambios de los servicios tienen que pasarse manualmente. Tiene redundancia de componentes y de suministro eléctrico y de red. El CPD es TIER III.
|
||||||
|
|
||||||
|
Salvo que se indique lo contrario en el enunciado, se supone que usted posee permisos de administrador.
|
||||||
|
|
||||||
|
### Preguntas
|
||||||
|
|
||||||
|
1. ¿Cuántas direcciones hay disponibles para hosts en la subred del organismo?
|
||||||
|
a) 4094
|
||||||
|
b) 65534
|
||||||
|
c) 4096
|
||||||
|
d) 65536
|
||||||
|
|
||||||
|
2. Al arrancar el servidor donde se aloja la web de profesores, aparecen errores en la partición sda8. ¿Qué comando hay que usar para reparar de forma automática los errores en el sistema de ficheros correspondiente, que no ha podido montarse?
|
||||||
|
a) mount –T /dev/sda8
|
||||||
|
b) fsck -y /dev/sda8
|
||||||
|
c) fsck -m /dev/sda8
|
||||||
|
d) checkdisk /dev/sda8
|
||||||
|
|
||||||
|
3. Analizando la seguridad del sistema, surge la duda de si abrir o no el puerto 80 en un servidor web expuesto mediante
|
||||||
|
HTTPS. El Centro Criptológico Nacional recomienda:
|
||||||
|
a) Exhibir una web estática en el puerto 80 indicando que esa no es la web actual.
|
||||||
|
b) Cambiar el puerto HTTPS al 8892.
|
||||||
|
c) Utilizar un analizador de peticiones en las cabeceras HTTP en el puerto 80.
|
||||||
|
d) Disponer del puerto TCP/80, configurando el servidor web para que lleve a cabo una redirección automática de HTTP a
|
||||||
|
HTTPS.
|
||||||
|
|
||||||
|
4. El CPD donde está alojado el sistema tiene una puerta cuyo control de acceso es con tarjeta inteligente y PIN, que sólo
|
||||||
|
conocen los operadores. Hace dos días, se supo que alguien no autorizado había entrado esperando agazapado a que
|
||||||
|
alguien autorizado entrase, pasando detrás de él sin que éste advirtiera que tenía a un intruso detrás. Este incidente de
|
||||||
|
ingeniería social en seguridad física se conoce con el nombre de:
|
||||||
|
a) Tailgating o piggybacking.
|
||||||
|
b) Quid pro quo.
|
||||||
|
c) Pretexto.
|
||||||
|
d) Disrupción.
|
||||||
|
|
||||||
|
5. El organismo tiene, para controlar la seguridad física del CPD, un circuito cerrado de televisión con cámaras que usa una
|
||||||
|
red coaxial y se necesita interconectar esta red a la red local Ethernet para poder monitorizarlas. ¿Con qué dispositivo de red puede hacerlo?
|
||||||
|
a) Un cortafuegos (firewall).
|
||||||
|
b) Una pasarela (gateway).
|
||||||
|
c) Un conmutador (switch).
|
||||||
|
d) Un repetidor (repeater).
|
||||||
|
|
||||||
|
6. En el sistema se utilizan los protocolos DNS y FTP seguro. De acuerdo con el modelo TCP/IP, estos protocolos se
|
||||||
|
diferencian en que:
|
||||||
|
a) DNS es un protocolo de usuario y FTP es un protocolo de soporte.
|
||||||
|
b) DNS es siempre un protocolo orientado a la conexión mientras que FTP no.
|
||||||
|
c) No existe diferencia entre ambos protocolos, ambos son protocolos de soporte.
|
||||||
|
d) FTP es un protocolo de usuario y DNS es un protocolo de soporte.
|
||||||
|
|
||||||
|
7. En un switch Cisco que hay en la organización, se ejecuta el comando “switchport access vlan 1”. Esto permitirá:
|
||||||
|
a) Asignar un puerto a la VLAN 1.
|
||||||
|
b) Asignar el puerto 1 del switch a la VLAN donde estamos situados en la consola del switch.
|
||||||
|
c) Visualizar todos los hosts asignados a la VLAN 1.
|
||||||
|
d) Asignar todos los hosts conectados a cualquier puerto del switch a la VLAN 1.
|
||||||
|
|
||||||
|
8. La base de datos se ha corrompido y además, los usuarios no pueden acceder a la información. Se tiene toda la
|
||||||
|
información de cómo se produjo el incidente y cuál es la persona que lo ha causado y por qué, pero, este incidente, ¿a
|
||||||
|
qué dimensión o dimensiones de la seguridad afecta?
|
||||||
|
a) A la disponibilidad porque no está accesible la base de datos y la integridad porque el fichero de la base de datos está corrupto.
|
||||||
|
b) A la confidencialidad porque los datos, una vez dañados, pueden ser accesibles por cualquiera.
|
||||||
|
c) Sólo a la disponibilidad porque la base de datos está temporalmente fuera de servicio hasta que se repare si es posible el fichero donde se aloja.
|
||||||
|
d) A la trazabilidad, porque no podremos averiguar lo que ha pasado por mucho que nos esforcemos, la seguridad es así.
|
||||||
|
|
||||||
|
9. La red de área local está implementada con Gigabit Ethernet y, al conectar un nuevo dispositivo a la red, en su tarjeta,
|
||||||
|
parpadea una luz de color naranja. ¿A qué puede deberse?
|
||||||
|
a) A que está mal configurada la VLAN donde está ubicado el dispositivo.
|
||||||
|
b) A que la tarjeta de red del dispositivo transmite a menor velocidad de la que permite la red.
|
||||||
|
c) A que la tarjeta de red está estropeada y no hay conexión entre el dispositivo y la red.
|
||||||
|
d) A que el cable del dispositivo es coaxial y no Ethernet.
|
||||||
|
|
||||||
|
10. Nos anuncian que hay una vulnerabilidad que afecta a una determinada versión del kernel de Linux Ubuntu. ¿Con qué
|
||||||
|
comando podemos saber qué versión del kernel tiene nuestro sistema operativo Ubuntu?
|
||||||
|
a) sudo dpkg -i linux*.deb
|
||||||
|
b) uname -r
|
||||||
|
c) uname -o
|
||||||
|
d) kexec -l
|
||||||
|
|
||||||
|
11. Para descargar algunos ficheros del servidor BIBLIOTECA, los administradores están sopesando entre el uso de SFTP y
|
||||||
|
FTPS. Indique, de las siguientes afirmaciones, la INCORRECTA:
|
||||||
|
a) SFTP usa típicamente el puerto 22 de SSH mientras que FTPS usa el puerto en el que tengamos definido el protocolo
|
||||||
|
SSL/TLS.
|
||||||
|
b) SFTP usa autenticación con certificado (clave pública) mientras que FTPS usa autenticación con usuario y contraseña.
|
||||||
|
c) FTPS usa dos puertos, uno para los comandos y otro para descargarse los datos mientras que SFTP usa el mismo puerto
|
||||||
|
para ambas tareas.
|
||||||
|
d) FTPS no contiene comandos estandarizados para manipular directorios o listar atributos, mientras que SFTP sí.
|
||||||
|
|
||||||
|
12. Para la detección de malware complejo y movimiento lateral relacionado con APTs (Advanced Persistent Threats), se ha
|
||||||
|
instalado en los PC de los profesores, la solución del CCN, CLAUDIA. ¿Cómo define el Centro Criptológico Nacional una
|
||||||
|
APT?
|
||||||
|
a) Es un tipo de ransomware como, por ejemplo, WannaCry, que te cifra los archivos del PC y se requiere la clave de descifrado
|
||||||
|
a cambio del pago de un rescate.
|
||||||
|
b) Es un ataque de suplantación de identidad de un usuario corriente en un organismo para después mediante escalada de
|
||||||
|
privilegios obtener las credenciales de un alto cargo de la empresa u organización.
|
||||||
|
c) Es un ataque selectivo de ciberespionaje o cibersabotaje llevado a cabo bajo el auspicio o la dirección de un país u
|
||||||
|
organización adversaria, por razones que van más allá de las meramente financieras/delictivas o de protesta política.
|
||||||
|
d) Es un ataque masivo a una organización ocurrido por un fallo que no se había advertido hasta ese momento y, por tanto, no se cuenta con la salvaguarda o parche para prevenirlo.
|
||||||
|
|
||||||
|
13. Para mejorar la escalabilidad del sistema, está estudiando implantar una arquitectura de microservicios con Kubernetes.
|
||||||
|
¿Qué protocolos para servicios pueden utilizarse con Kubernetes?
|
||||||
|
a) SCTP, TCP (por defecto) y UDP.
|
||||||
|
b) HTTP (por defecto), HTTPS y FTP.
|
||||||
|
c) SSH, SFTP (por defecto) y UDP.
|
||||||
|
d) UDP (por defecto) y TCP.
|
||||||
|
|
||||||
|
14. Para poder atender las llamadas de las guardias de sistemas, se han comprado veinte móviles. Su responsable le pide realizar el enrolamiento de estos móviles, pero, ¿en qué consiste esta tarea?
|
||||||
|
a) Emparejar, en el sistema MDM (Mobile Device Management) de la organización, a cada usuario con su móvil.
|
||||||
|
b) Dar de alta en una base de datos de administración todos los dispositivos móviles.
|
||||||
|
c) Insertar la tarjeta SIM correspondiente a cada móvil.
|
||||||
|
d) Formatear a fábrica todos los dispositivos móviles.
|
||||||
|
|
||||||
|
15. Se está instalando la nueva climatización del CPD y se plantea utilizar una toma derivada de la climatización del resto del edificio. ¿Cuál es la razón principal por la que NO se aconseja hacer eso?
|
||||||
|
a) Porque la humedad recomendada para un CPD es mucho menor que la recomendada para las personas.
|
||||||
|
b) Porque la temperatura recomendada para un CPD es mucho mayor que la recomendada para las personas.
|
||||||
|
c) Porque el filtro de impurezas y polvo para las personas es mucho más sensible que lo que se recomienda para un CPD.
|
||||||
|
d) Porque el aire acondicionado para un CPD siempre debe provenir del techo y para las personas puede provenir lateralmente.
|
||||||
|
|
||||||
|
16. Se ha descargado del sitio web del CCN-CERT una herramienta de antimalware y justo debajo aparece un hash de
|
||||||
|
comprobación. ¿Qué tipo de medida de seguridad es este hash en este contexto?
|
||||||
|
a) Una medida antimalware, pues el hash aplicado al fichero descargado nos sanitiza el fichero y ya se puede usar sin
|
||||||
|
problemas, pues está limpio de malware, lo cual en una herramienta antimalware es altamente necesario.
|
||||||
|
b) Una medida para asegurar la integridad del fichero de descarga, pues si al calcular nosotros el hash del fichero descargado no coincide con el que nos aparece en la página del CCN, el fichero descargado no sería válido para su uso.
|
||||||
|
c) El hash es la firma del CCN como autoridad de certificación de productos que garantiza que el software descargado es apto
|
||||||
|
para ser usado en sistemas categorizados como de nivel ALTO o incluso en sistemas clasificados como reservado nacional.
|
||||||
|
d) El hash es la firma del CCN de la página web donde se presenta la herramienta que estamos intentando descargar y es una medida para evitar que los hackers puedan manipular la página y subir otro fichero en vez del que se pretende descargar.
|
||||||
|
|
||||||
|
17. Se ha implantado una solución VoIP que usa el protocolo SIP pero, al establecer sesiones con usuarios de fuera del
|
||||||
|
organismo, la llamada se corta o congela y después, se restablece. Este problema no sucede en sesiones entre usuarios
|
||||||
|
internos del organismo, ¿por qué puede ser?
|
||||||
|
a) Se están usando incorrectamente los códecs.
|
||||||
|
b) La salida a Internet está empleando NAT.
|
||||||
|
c) El tráfico entre origen y destino está interceptado en el cortafuegos de Internet.
|
||||||
|
d) La CPU del ordenador es insuficiente y provoca estos problemas.
|
||||||
|
|
||||||
|
18. Se ha instalado un servidor de correo con Postfix y se está decidiendo si utilizar POP3 o IMAP en los clientes de correo. ¿Cuál de las siguientes opciones es INCORRECTA?
|
||||||
|
a) Con IMAP, los mensajes se almacenan en un servidor remoto y los usuarios pueden iniciar sesión en varios clientes de correo
|
||||||
|
electrónico y leer los mismos mensajes.
|
||||||
|
b) POP3 solo admite la sincronización de correo unidireccional, lo que solo permite a los usuarios descargar correos electrónicos
|
||||||
|
desde un servidor a un cliente.
|
||||||
|
c) Con IMAP, el correo enviado y recibido se almacena en el servidor hasta que el usuario lo elimina permanentemente.
|
||||||
|
d) Con POP3, si los usuarios organizan sus correos electrónicos en un dispositivo mediante carpetas, ya no tendrán que hacerlo
|
||||||
|
en el resto de dispositivos porque se replica la organización en carpetas.
|
||||||
|
|
||||||
|
19. Se necesita saber los usuarios que acceden a la base de datos de la biblioteca y desde qué host o IP. Para averiguarlo, en la consola de administración de la base de datos, ejecutará el comando:
|
||||||
|
a) SELECT * FROM all_users
|
||||||
|
b) mysql> SELECT user FROM mysql.user
|
||||||
|
c) mysql> SELECT user,host FROM mysql.user
|
||||||
|
d) sudo mysql -u root -p
|
||||||
|
|
||||||
|
20. Se quiere aplicar políticas de seguridad al grupo de usuarios Profesores utilizando Directorio Activo y GPOs. Una buena
|
||||||
|
práctica es definir primero:
|
||||||
|
a) Una Unidad Organizativa (OU) para el grupo de usuarios Profesores.
|
||||||
|
b) Las ACLs (Access Control Lists) que tendrán las GPO del grupo de usuarios Profesores.
|
||||||
|
c)
|
||||||
|
Un nuevo bosque de Directorio Activo.
|
||||||
|
d) Relaciones de confianza entre el Directorio Activo actual y el dominio de seguridad del grupo de usuarios Profesores.
|
||||||
|
|
||||||
|
### Preguntas de reserva
|
||||||
|
|
||||||
|
1. Como administrador de correo, quiere cambiar el mensaje HELO que aparece cuando se establecen conexiones al
|
||||||
|
servidor SMTP. Actualmente aparece el nombre del servidor, pero quiere que muestre "CORREOBIBLIOTECA" y además,
|
||||||
|
que el cambio se aplique en todos los servidores SMTP que existan, no solo en aquel en el que está trabajando. ¿Cómo
|
||||||
|
lo haría?
|
||||||
|
a) Editando el parámetro $client en el fichero /etc/postfix/main.cf y reiniciando posteriormente el servidor Postfix.
|
||||||
|
b) Accediendo al servidor SMTP y ejecutando EHLO -name CORREOBIBLIOTECA en la consola.
|
||||||
|
c) Editando el parámetro smtp_helo en el fichero /etc/postfix/master.cf y reiniciando posteriormente el servidor Postfix.
|
||||||
|
d) Editando el parámetro $helo_name en /etc/postfix/main.cf y reiniciando posteriormente el servidor Postfix.
|
||||||
|
|
||||||
|
2. Como no hay suficientes tomas Ethernet en la zona física donde están los profesores, se va a instalar una red WiFi con
|
||||||
|
protocolo WPA3-Enterprise. En este caso, para acceder a la red WiFi habrá que utilizar:
|
||||||
|
a) Una contraseña de 64 bits.
|
||||||
|
b) Una contraseña de 128 bits.
|
||||||
|
c) Un servidor RADIUS o cualquier solución que permita EAP-TLS.
|
||||||
|
d) Una contraseña de 192 bits usando el algoritmo GMCP-256.
|
||||||
|
|
||||||
|
3. En la base de datos MySQL de Profesores, nos piden que añadamos una tabla de los profesores para el curso de INGLES
|
||||||
|
BASICO, que se llamará "ProfesoresIngles" con sus nombres y apellidos. Para ello, y tras acceder como root a MYSQL,
|
||||||
|
ingresaremos el comando:
|
||||||
|
a) CREATE TABLE ProfesoresIngles (id INT NOT NULL PRIMARY KEY AUTO_INCREMENT, nombre VARCHAR(30), apellido1
|
||||||
|
VARCHAR(30), apellido2 VARCHAR(30));
|
||||||
|
b) CREATE TABLE ProfesoresIngles KEY id, nombre, apellidos;
|
||||||
|
c) CREAR TABLA ProfesoresIngles KEY apellidos, nombre;
|
||||||
|
d) CREATE TABLE ProfesoresIngles (nombre, apellido);
|
||||||
|
|
||||||
|
4. Ha instalado el servicio Nagios para monitorizar los servidores y servicios en su sistema y quiere monitorizar, además, la URL de profesores (https://profesores.es), pero antes quiere comprobar que este servicio está configurado correctamente.
|
||||||
|
Para ello, utilizará el comando:
|
||||||
|
a) nagios –v /usr/local/nagios/etc/nagios.cfg
|
||||||
|
b) /mnt/nagios –check
|
||||||
|
c) systemctl status nagios
|
||||||
|
d) https://profesores.es nagios
|
||||||
|
|
||||||
|
5. Se ha instalado un servidor web con HTTPS y certificado SSL de servidor para proteger la conexión entre los clientes y el
|
||||||
|
servidor, para lo cual la conexión usará TLS. Según las recomendaciones del Centro Criptológico Nacional, ¿cuál es la
|
||||||
|
versión mínima y la recomendada a usar en TLS?
|
||||||
|
a) La versión mínima aceptable es la 1.1 y se recomienda usar esa misma versión 1.1
|
||||||
|
b) La versión mínima aceptable es la 1.0 y se recomienda usar 1.1
|
||||||
|
c)
|
||||||
|
La versión mínima aceptable es la 1.2 y se recomienda usar la 1.3
|
||||||
|
d) La versión mínima aceptable es la 1.1 y se recomienda usar la 1.3
|
||||||
|
|
@ -0,0 +1,162 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="es">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>TAI – AGE | Cuestionarios</title>
|
||||||
|
<link rel="stylesheet" href="../css/style.css">
|
||||||
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.0/css/all.min.css">
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;700&display=swap" rel="stylesheet">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<!-- ── Topbar ─────────────────────────────────────────── -->
|
||||||
|
<nav class="topbar">
|
||||||
|
<span class="topbar-brand"><i class="fas fa-graduation-cap"></i> TAI – AGE</span>
|
||||||
|
<nav class="topbar-nav">
|
||||||
|
<a href="../index.html">Inicio</a>
|
||||||
|
<a href="../curso.html">Temario</a>
|
||||||
|
<a href="index.html" class="active">Cuestionarios</a>
|
||||||
|
<a href="../noticias.html">Noticias</a>
|
||||||
|
</nav>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<!-- ── Layout ────────────────────────────────────────── -->
|
||||||
|
<div class="quiz-layout" style="margin-top:calc(var(--topbar-h) + 1.5rem)">
|
||||||
|
|
||||||
|
<div class="quiz-header">
|
||||||
|
<h1><i class="fas fa-file-alt"></i> Cuestionarios TAI</h1>
|
||||||
|
<p>Exámenes reales de convocatorias anteriores (2019 – 2024).<br>
|
||||||
|
Fórmula AGE: <strong>Nota = (Aciertos − Fallos/3) / Total × 10</strong></p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Marcador -->
|
||||||
|
<div class="quiz-scoreboard">
|
||||||
|
<div class="score-chip aciertos">
|
||||||
|
<span class="val" id="val-aciertos">0</span>
|
||||||
|
<span class="lbl">Aciertos</span>
|
||||||
|
</div>
|
||||||
|
<div class="score-chip fallos">
|
||||||
|
<span class="val" id="val-fallos">0</span>
|
||||||
|
<span class="lbl">Fallos</span>
|
||||||
|
</div>
|
||||||
|
<div class="score-chip total">
|
||||||
|
<span class="val" id="val-progreso">0 / 0</span>
|
||||||
|
<span class="lbl">Progreso</span>
|
||||||
|
</div>
|
||||||
|
<div class="score-chip" style="border-color:var(--warning)">
|
||||||
|
<span class="val" id="val-nota" style="color:var(--warning)">—</span>
|
||||||
|
<span class="lbl">Nota est.</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Controles -->
|
||||||
|
<div class="quiz-controls">
|
||||||
|
<select id="sel-examen" class="quiz-select">
|
||||||
|
<option value="">Selecciona un examen…</option>
|
||||||
|
<option value="data/TAI_2019.json">TAI 2019</option>
|
||||||
|
<option value="data/TAI_2023.json">TAI 2023</option>
|
||||||
|
<option value="data/TAI_2024A.json">TAI 2024 – Modelo A</option>
|
||||||
|
<option value="data/TAI_2024B.json">TAI 2024 – Modelo B</option>
|
||||||
|
</select>
|
||||||
|
<button id="btn-iniciar" class="btn btn-primary" disabled>
|
||||||
|
<i class="fas fa-play"></i> Iniciar
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- PDFs del examen seleccionado -->
|
||||||
|
<div id="exam-pdfs" class="exam-pdfs" style="display:none"></div>
|
||||||
|
|
||||||
|
<!-- Empty state -->
|
||||||
|
<div id="seccion-empty" class="empty-state">
|
||||||
|
<i class="fas fa-question-circle" style="color:var(--accent)"></i>
|
||||||
|
<p>Selecciona un examen y pulsa <strong>Iniciar</strong> para comenzar a practicar.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Pregunta activa -->
|
||||||
|
<div id="seccion-quiz" style="display:none">
|
||||||
|
<div class="question-card">
|
||||||
|
<div class="question-num" id="pregunta-num">Pregunta 1 de …</div>
|
||||||
|
<div class="question-text" id="pregunta-txt"></div>
|
||||||
|
<div id="contexto-pregunta" class="supuesto-context" style="display:none"></div>
|
||||||
|
<ul class="options-list" id="opciones"></ul>
|
||||||
|
<div class="question-feedback" id="feedback"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="quiz-nav-row">
|
||||||
|
<button id="btn-siguiente" class="btn btn-primary" style="display:none">
|
||||||
|
Siguiente <i class="fas fa-chevron-right"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Selector de supuestos prácticos -->
|
||||||
|
<div id="seccion-supuesto-sel" style="display:none">
|
||||||
|
<div class="sup-sel-header">
|
||||||
|
<h2><i class="fas fa-clipboard-list"></i> Supuestos prácticos</h2>
|
||||||
|
<p>Has completado las preguntas tipo test. Ahora elige <strong>uno de los dos supuestos prácticos</strong> para practicarlo con su material de referencia.</p>
|
||||||
|
</div>
|
||||||
|
<div id="sup-cards" class="sup-cards-grid"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Pantalla final -->
|
||||||
|
<div id="seccion-final" style="display:none">
|
||||||
|
<div class="final-screen">
|
||||||
|
<h2><i class="fas fa-flag-checkered"></i> ¡Examen completado!</h2>
|
||||||
|
<div class="final-score-big" id="final-nota">—</div>
|
||||||
|
<div class="final-score-sub">sobre 10 (fórmula AGE)</div>
|
||||||
|
|
||||||
|
<div class="quiz-scoreboard" style="margin:1.5rem 0">
|
||||||
|
<div class="score-chip aciertos">
|
||||||
|
<span class="val" id="final-aciertos">0</span>
|
||||||
|
<span class="lbl">Aciertos</span>
|
||||||
|
</div>
|
||||||
|
<div class="score-chip fallos">
|
||||||
|
<span class="val" id="final-fallos">0</span>
|
||||||
|
<span class="lbl">Fallos</span>
|
||||||
|
</div>
|
||||||
|
<div class="score-chip">
|
||||||
|
<span class="val" id="final-sin" style="color:var(--text-muted)">0</span>
|
||||||
|
<span class="lbl">Sin resp.</span>
|
||||||
|
</div>
|
||||||
|
<div class="score-chip total">
|
||||||
|
<span class="val" id="final-total">0</span>
|
||||||
|
<span class="lbl">Total</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style="display:flex;gap:1rem;justify-content:center;flex-wrap:wrap">
|
||||||
|
<button class="btn btn-primary" onclick="reiniciar()">
|
||||||
|
<i class="fas fa-redo"></i> Volver a intentarlo
|
||||||
|
</button>
|
||||||
|
<a href="../curso.html" class="btn btn-outline">
|
||||||
|
<i class="fas fa-book-open"></i> Repasar temario
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Análisis de fallos -->
|
||||||
|
<div id="repaso-wrap" class="repaso-wrap" style="display:none"></div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
function reiniciar() {
|
||||||
|
['seccion-final','seccion-quiz','seccion-supuesto-sel'].forEach(id => {
|
||||||
|
document.getElementById(id).style.display = 'none';
|
||||||
|
});
|
||||||
|
document.getElementById('seccion-empty').style.display = 'block';
|
||||||
|
document.getElementById('sel-examen').value = '';
|
||||||
|
document.getElementById('btn-iniciar').disabled = true;
|
||||||
|
document.getElementById('btn-iniciar').innerHTML = '<i class="fas fa-play"></i> Iniciar';
|
||||||
|
['val-aciertos','val-fallos'].forEach(id => document.getElementById(id).textContent = '0');
|
||||||
|
document.getElementById('val-progreso').textContent = '0 / 0';
|
||||||
|
document.getElementById('val-nota').textContent = '—';
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<script src="js/quiz.js"></script>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
@ -0,0 +1,125 @@
|
||||||
|
const selector = document.getElementById("selector");
|
||||||
|
const quiz = document.getElementById("quiz");
|
||||||
|
const resultado = document.getElementById("resultado");
|
||||||
|
const feedback = document.getElementById("feedback");
|
||||||
|
const preguntaActualContenedor = document.getElementById("pregunta-actual");
|
||||||
|
|
||||||
|
let preguntasCargadas = [];
|
||||||
|
let estado = {
|
||||||
|
indiceActual: 0,
|
||||||
|
aciertos: 0,
|
||||||
|
fallos: 0
|
||||||
|
};
|
||||||
|
|
||||||
|
// --- CARGAR EXAMEN ---
|
||||||
|
selector.addEventListener("change", async () => {
|
||||||
|
const url = selector.value;
|
||||||
|
if (!url) return;
|
||||||
|
|
||||||
|
const datos = await fetch(url).then(r => r.json());
|
||||||
|
// Ajustamos por si el JSON viene con una propiedad .preguntas o es un array directo
|
||||||
|
preguntasCargadas = datos.preguntas || datos;
|
||||||
|
|
||||||
|
reiniciarEstadisticas();
|
||||||
|
mostrarSiguientePregunta();
|
||||||
|
});
|
||||||
|
|
||||||
|
function reiniciarEstadisticas() {
|
||||||
|
estado.indiceActual = 0;
|
||||||
|
estado.aciertos = 0;
|
||||||
|
estado.fallos = 0;
|
||||||
|
quiz.innerHTML = ""; // Limpiamos el modo "lista completa"
|
||||||
|
resultado.textContent = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- MOSTRAR UNA POR UNA ---
|
||||||
|
function mostrarSiguientePregunta() {
|
||||||
|
feedback.textContent = "";
|
||||||
|
|
||||||
|
if (estado.indiceActual >= preguntasCargadas.length) {
|
||||||
|
finalizarExamen();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const p = preguntasCargadas[estado.indiceActual];
|
||||||
|
actualizarMarcador();
|
||||||
|
|
||||||
|
preguntaActualContenedor.innerHTML = `
|
||||||
|
<div class="pregunta">
|
||||||
|
<p><strong>Pregunta ${estado.indiceActual + 1} de ${preguntasCargadas.length}</strong></p>
|
||||||
|
<p>${p.pregunta}</p>
|
||||||
|
${Object.entries(p.opciones).map(([letra, texto]) => `
|
||||||
|
<label class="opcion">
|
||||||
|
<input type="radio" name="respuesta" value="${letra}">
|
||||||
|
${letra}) ${texto}
|
||||||
|
</label>
|
||||||
|
`).join("")}
|
||||||
|
<br>
|
||||||
|
<button id="comprobar">Comprobar y Siguiente</button>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
document.getElementById("comprobar").onclick = () => validarRespuesta(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
function validarRespuesta(p) {
|
||||||
|
const marcada = document.querySelector(`input[name="respuesta"]:checked`);
|
||||||
|
|
||||||
|
if (!marcada) {
|
||||||
|
feedback.textContent = "⚠️ Por favor, selecciona una opción.";
|
||||||
|
feedback.style.color = "orange";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (marcada.value === p.correcta) {
|
||||||
|
estado.aciertos++;
|
||||||
|
alert("✔ ¡Correcto!"); // Opcional, puedes usar el div feedback
|
||||||
|
} else {
|
||||||
|
estado.fallos++;
|
||||||
|
alert(`✘ Incorrecto. La respuesta era la ${p.correcta.toUpperCase()}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
estado.indiceActual++;
|
||||||
|
mostrarSiguientePregunta();
|
||||||
|
}
|
||||||
|
|
||||||
|
function actualizarMarcador() {
|
||||||
|
const contestadas = estado.indiceActual; // Preguntas que ya pasaron
|
||||||
|
|
||||||
|
// Si aún no hemos contestado ninguna, la nota es 0.00
|
||||||
|
let notaSobreDiez = 0;
|
||||||
|
|
||||||
|
if (contestadas > 0) {
|
||||||
|
// Fórmula de puntos netos (AGE): Aciertos - (Fallos / 3)
|
||||||
|
const puntosNetos = estado.aciertos - (estado.fallos / 3);
|
||||||
|
|
||||||
|
// Calculamos la nota sobre 10 basada SOLO en las contestadas hasta ahora
|
||||||
|
notaSobreDiez = (puntosNetos / contestadas) * 10;
|
||||||
|
|
||||||
|
// Evitamos notas negativas si hay muchísimos fallos
|
||||||
|
if (notaSobreDiez < 0) notaSobreDiez = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
resultado.innerHTML = `
|
||||||
|
<div class="marcador-container">
|
||||||
|
<div class="stat">Aciertos: <span class="verde">${estado.aciertos}</span></div>
|
||||||
|
<div class="stat">Fallos: <span class="rojo">${estado.fallos}</span></div>
|
||||||
|
<div class="stat">Progreso: <span>${contestadas} / ${preguntasCargadas.length}</span></div>
|
||||||
|
<div class="nota-actual">Nota actual: <strong>${notaSobreDiez.toFixed(2)}</strong></div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function finalizarExamen() {
|
||||||
|
preguntaActualContenedor.innerHTML = "<h2>¡Examen finalizado!</h2>";
|
||||||
|
const notaFinal = ((estado.aciertos - (estado.fallos / 3)) / preguntasCargadas.length * 10).toFixed(2);
|
||||||
|
|
||||||
|
resultado.innerHTML = `
|
||||||
|
<div style="font-size: 1.2em; border: 2px solid #333; padding: 20px;">
|
||||||
|
<h3>Resumen Final:</h3>
|
||||||
|
<p>Aciertos: ${estado.aciertos}</p>
|
||||||
|
<p>Fallos: ${estado.fallos}</p>
|
||||||
|
<p>Nota sobre 10: <strong>${notaFinal}</strong></p>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,378 @@
|
||||||
|
/**
|
||||||
|
* cuestionarios/js/quiz.js
|
||||||
|
* Lógica del cuestionario TAI con fórmula de corrección AGE.
|
||||||
|
* Fórmula: Nota = (Aciertos - Fallos/3) / TotalPreguntas * 10
|
||||||
|
*/
|
||||||
|
|
||||||
|
// ── Estado ──────────────────────────────────────────────────
|
||||||
|
let preguntas = [];
|
||||||
|
let supuestosData = {}; // { "Supuesto I": [...], "Supuesto II": [...] }
|
||||||
|
let faseActual = 'test'; // 'test' | 'supuesto'
|
||||||
|
let indice = 0;
|
||||||
|
let aciertos = 0;
|
||||||
|
let fallos = 0;
|
||||||
|
let respondida = false;
|
||||||
|
let preguntasFalladas = []; // { pregunta, elegida }
|
||||||
|
|
||||||
|
// ── Mapa de temas y palabras clave ──────────────────────────
|
||||||
|
const TEMAS_KW = [
|
||||||
|
{ id:'const', label:'Constitución Española', link:'../curso.html?bloque=1', kw:['constitución','constitucional','rey ','cortes generales','senado','congreso','diputad','tribunal constitucional','defensor del pueblo','artículo 62','artículo 63','título i','título ii','artículo 1 ','capítulo'] },
|
||||||
|
{ id:'p39', label:'Procedimiento Adm. (Ley 39/2015)', link:'../curso.html?bloque=1', kw:['ley 39','procedimiento administrativo','recurso de alzada','silencio administrativo','notificación','expediente administrativo','recurso potestativo','recurso extraordinario'] },
|
||||||
|
{ id:'p40', label:'Régimen Jurídico (Ley 40/2015)', link:'../curso.html?bloque=1', kw:['ley 40','órgano colegiado','delegación de competencia','avocación','administración general del estado','convenio interadministrativo'] },
|
||||||
|
{ id:'lcsp', label:'Contratación Pública (LCSP)', link:'../curso.html?bloque=1', kw:['contratos del sector público','lcsp','licitación','adjudicación','pliego','contrato menor','concesión de servicios','poder adjudicador'] },
|
||||||
|
{ id:'trebep', label:'Función Pública (TREBEP)', link:'../curso.html?bloque=1', kw:['trebep','funcionario','empleado público','oposición','provisión de puestos','situaciones administrativas','excedencia','régimen disciplinario','carrera profesional'] },
|
||||||
|
{ id:'hac', label:'Hacienda Pública / Presupuestos', link:'../curso.html?bloque=1', kw:['presupuesto','hacienda pública','igae','tribunal de cuentas','crédito presupuestario','gasto público','control financiero','intervención general'] },
|
||||||
|
{ id:'html', label:'HTML / CSS / JavaScript', link:'../curso.html?bloque=3', kw:['html','css','javascript','dom','<input','etiqueta','formulario web','diseño web','responsive','selector css'] },
|
||||||
|
{ id:'bd', label:'Bases de Datos / SQL', link:'../curso.html?bloque=3', kw:['sql','select ','join','base de datos','normalización','índice','relacional','trigger','procedimiento almacenado','sgbd','tabla '] },
|
||||||
|
{ id:'prog', label:'Programación / Algoritmos', link:'../curso.html?bloque=3', kw:['algoritmo','lenguaje de programación','java ','python','c# ','compilad','interpreta','orientado a objetos','array','recursiv','metodología ágil','scrum','git'] },
|
||||||
|
{ id:'redes', label:'Redes y Comunicaciones', link:'../curso.html?bloque=4', kw:['tcp/ip','protocolo','router','switch','vlan','dirección ip','máscara de red','ethernet','arp','dns','dhcp','ospf','bgp','mpls'] },
|
||||||
|
{ id:'seg', label:'Seguridad Informática', link:'../curso.html?bloque=4', kw:['cifrado','criptografía','firma digital','certificado digital','ssl','tls','vpn','firewall','ciberincidente','autenticación','hash','ransomware','ids ','ips '] },
|
||||||
|
{ id:'so', label:'Sistemas Operativos / Hardware', link:'../curso.html?bloque=2', kw:['sistema operativo','linux','windows server','proceso','kernel','sistema de ficheros','raid','virtualización','contenedor','cpu','procesador','memoria ram'] },
|
||||||
|
];
|
||||||
|
|
||||||
|
function detectarTema(txt) {
|
||||||
|
const t = txt.toLowerCase();
|
||||||
|
for (const tema of TEMAS_KW) {
|
||||||
|
if (tema.kw.some(k => t.includes(k))) return tema;
|
||||||
|
}
|
||||||
|
return { id:'otro', label:'Otros / Material general', link:'../curso.html' };
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Elementos DOM ────────────────────────────────────────────
|
||||||
|
const selExamen = document.getElementById('sel-examen');
|
||||||
|
const btnIniciar = document.getElementById('btn-iniciar');
|
||||||
|
const btnSiguiente = document.getElementById('btn-siguiente');
|
||||||
|
const seccionQuiz = document.getElementById('seccion-quiz');
|
||||||
|
const seccionFinal = document.getElementById('seccion-final');
|
||||||
|
const seccionEmpty = document.getElementById('seccion-empty');
|
||||||
|
const seccionSupSel = document.getElementById('seccion-supuesto-sel');
|
||||||
|
const supCardsWrap = document.getElementById('sup-cards');
|
||||||
|
const contextoPanel = document.getElementById('contexto-pregunta');
|
||||||
|
const examPdfsPanel = document.getElementById('exam-pdfs');
|
||||||
|
|
||||||
|
// ── Mapa de PDFs por examen ───────────────────────────────────
|
||||||
|
const EXAM_PDFS = {
|
||||||
|
'data/TAI_2019.json': [
|
||||||
|
{ label: 'Cuestionario oficial', icon: 'fa-file-alt', url: 'pdfs/cues_1er_ejer_TAI-L_oep19_154AB89SD658.pdf' },
|
||||||
|
{ label: 'Plantilla definitiva', icon: 'fa-check-square', url: 'pdfs/Plantilla_defTAI-L1ejer_154AB89SD658.pdf' },
|
||||||
|
{ label: 'Plantilla provisional', icon: 'fa-clipboard', url: 'pdfs/plant_prov_1er_ejer_TAI-L_oep19_154AB89SD658.pdf' },
|
||||||
|
{ label: 'Material adicional (supuesto)', icon: 'fa-book-open', url: 'pdfs/07TAIL_154AB89SD658.pdf' },
|
||||||
|
],
|
||||||
|
'data/TAI_2023.json': [
|
||||||
|
{ label: 'Cuestionario oficial', icon: 'fa-file-alt', url: 'pdfs/Cuestionario TAI-L_2023.pdf' },
|
||||||
|
{ label: 'Plantilla de respuestas', icon: 'fa-check-square', url: 'pdfs/PlantillaRespuestas TAI-L_2023.pdf' },
|
||||||
|
],
|
||||||
|
'data/TAI_2024A.json': [
|
||||||
|
{ label: 'Cuestionario oficial', icon: 'fa-file-alt', url: 'pdfs/Cuestionario_TAI_LI_2024_A_M8L91VL1CL_154AB89SD658.pdf' },
|
||||||
|
{ label: 'Plantilla de respuestas', icon: 'fa-check-square', url: 'pdfs/Plantilla_Respuestas_PROV_TAI_LI_2024_A_6J5MFQ8OEN_154AB89SD658.pdf' },
|
||||||
|
{ label: 'Material supuesto II', icon: 'fa-book-open', url: 'data/tai_2024A_supuesto2.md' },
|
||||||
|
],
|
||||||
|
'data/TAI_2024B.json': [
|
||||||
|
{ label: 'Cuestionario oficial', icon: 'fa-file-alt', url: 'pdfs/Cuestionario_TAI_LI_2024_B_DNFGFEK45R_154AB89SD658.pdf' },
|
||||||
|
{ label: 'Plantilla de respuestas', icon: 'fa-check-square', url: 'pdfs/Plantilla_Respuestas_PROV_TAI_LI_2024_B_JQE95HBC1R_154AB89SD658.pdf' },
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
const elPreguntaNum = document.getElementById('pregunta-num');
|
||||||
|
const elPreguntaTxt = document.getElementById('pregunta-txt');
|
||||||
|
const elOpciones = document.getElementById('opciones');
|
||||||
|
const elFeedback = document.getElementById('feedback');
|
||||||
|
|
||||||
|
const elAciertos = document.getElementById('val-aciertos');
|
||||||
|
const elFallos = document.getElementById('val-fallos');
|
||||||
|
const elProgreso = document.getElementById('val-progreso');
|
||||||
|
const elNota = document.getElementById('val-nota');
|
||||||
|
|
||||||
|
// ── Eventos ──────────────────────────────────────────────────
|
||||||
|
btnIniciar.addEventListener('click', iniciarExamen);
|
||||||
|
btnSiguiente.addEventListener('click', siguiente);
|
||||||
|
|
||||||
|
selExamen.addEventListener('change', () => {
|
||||||
|
btnIniciar.disabled = !selExamen.value;
|
||||||
|
renderExamPdfs(selExamen.value);
|
||||||
|
});
|
||||||
|
|
||||||
|
function renderExamPdfs(url) {
|
||||||
|
const pdfs = EXAM_PDFS[url];
|
||||||
|
if (!pdfs || !pdfs.length) { examPdfsPanel.style.display = 'none'; return; }
|
||||||
|
examPdfsPanel.innerHTML =
|
||||||
|
'<span class="exam-pdfs-label"><i class="fas fa-file-pdf"></i> Documentos INAP:</span>' +
|
||||||
|
pdfs.map(p =>
|
||||||
|
`<a href="${encodeURI(p.url)}" target="_blank" rel="noopener" class="exam-pdf-link">` +
|
||||||
|
`<i class="fas ${p.icon}"></i> ${escHtml(p.label)}</a>`
|
||||||
|
).join('');
|
||||||
|
examPdfsPanel.style.display = 'flex';
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Funciones ────────────────────────────────────────────────
|
||||||
|
async function iniciarExamen() {
|
||||||
|
const url = selExamen.value;
|
||||||
|
if (!url) return;
|
||||||
|
|
||||||
|
btnIniciar.disabled = true;
|
||||||
|
btnIniciar.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Cargando…';
|
||||||
|
|
||||||
|
try {
|
||||||
|
const datos = await fetch(url).then(r => r.json());
|
||||||
|
const todas = Array.isArray(datos) ? datos : (datos.preguntas || []);
|
||||||
|
// Separar preguntas tipo test de supuestos prácticos
|
||||||
|
const testQs = todas.filter(p => !p.contexto?.supuesto);
|
||||||
|
const supuestoQs = todas.filter(p => p.contexto?.supuesto);
|
||||||
|
preguntas = mezclar(testQs);
|
||||||
|
supuestosData = {};
|
||||||
|
for (const p of supuestoQs) {
|
||||||
|
const nombre = p.contexto.supuesto;
|
||||||
|
if (!supuestosData[nombre]) supuestosData[nombre] = [];
|
||||||
|
supuestosData[nombre].push(p);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
alert('Error cargando el examen. Comprueba la ruta del fichero.');
|
||||||
|
btnIniciar.disabled = false;
|
||||||
|
btnIniciar.innerHTML = '<i class="fas fa-play"></i> Iniciar';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
faseActual = 'test';
|
||||||
|
indice = 0;
|
||||||
|
aciertos = 0;
|
||||||
|
fallos = 0;
|
||||||
|
respondida = false;
|
||||||
|
preguntasFalladas = [];
|
||||||
|
|
||||||
|
mostrarSolo('quiz');
|
||||||
|
actualizarMarcador();
|
||||||
|
mostrarPregunta();
|
||||||
|
|
||||||
|
btnIniciar.disabled = false;
|
||||||
|
btnIniciar.innerHTML = '<i class="fas fa-redo"></i> Reiniciar';
|
||||||
|
}
|
||||||
|
|
||||||
|
function mostrarSolo(seccion) {
|
||||||
|
seccionEmpty.style.display = seccion === 'empty' ? 'block' : 'none';
|
||||||
|
seccionQuiz.style.display = seccion === 'quiz' ? 'block' : 'none';
|
||||||
|
seccionFinal.style.display = seccion === 'final' ? 'block' : 'none';
|
||||||
|
seccionSupSel.style.display = seccion === 'supsel' ? 'block' : 'none';
|
||||||
|
}
|
||||||
|
|
||||||
|
function mostrarSelectorSupuesto() {
|
||||||
|
supCardsWrap.innerHTML = '';
|
||||||
|
for (const [nombre, pregs] of Object.entries(supuestosData)) {
|
||||||
|
const ctx = pregs[0]?.contexto || {};
|
||||||
|
const ref = ctx.referencia_diagrama || ctx.referencia || null;
|
||||||
|
const esImg = ref && /\.(png|jpg|jpeg|gif|webp)$/i.test(ref);
|
||||||
|
const esPdf = ref && /\.pdf$/i.test(ref);
|
||||||
|
|
||||||
|
const card = document.createElement('div');
|
||||||
|
card.className = 'sup-card';
|
||||||
|
card.innerHTML = `
|
||||||
|
<div class="sup-card-header">
|
||||||
|
<i class="fas fa-file-code"></i>
|
||||||
|
<strong>${escHtml(nombre)}</strong>
|
||||||
|
<span class="sup-card-num">${pregs.length} preguntas</span>
|
||||||
|
</div>
|
||||||
|
<p class="sup-card-desc">${escHtml(ctx.descripcion || '')}</p>
|
||||||
|
${esImg ? `<div class="sup-material"><img src="data/${ref}" alt="Material ${escHtml(nombre)}" style="max-width:100%;border-radius:6px;margin:.5rem 0"></div>` : ''}
|
||||||
|
${esPdf ? `<div class="sup-material"><a href="data/${ref}" target="_blank" rel="noopener" class="btn btn-outline" style="font-size:.85rem"><i class="fas fa-file-pdf"></i> Ver enunciado (PDF)</a></div>` : ''}
|
||||||
|
<button class="btn btn-primary" style="margin-top:.75rem" onclick="iniciarSupuesto('${nombre.replace(/'/g, "\\'")}')"><i class="fas fa-play"></i> Practicar este supuesto</button>
|
||||||
|
`;
|
||||||
|
supCardsWrap.appendChild(card);
|
||||||
|
}
|
||||||
|
mostrarSolo('supsel');
|
||||||
|
}
|
||||||
|
|
||||||
|
function iniciarSupuesto(nombre) {
|
||||||
|
preguntas = supuestosData[nombre] || [];
|
||||||
|
faseActual = 'supuesto';
|
||||||
|
indice = 0;
|
||||||
|
aciertos = 0;
|
||||||
|
fallos = 0;
|
||||||
|
respondida = false;
|
||||||
|
preguntasFalladas = [];
|
||||||
|
mostrarSolo('quiz');
|
||||||
|
actualizarMarcador();
|
||||||
|
mostrarPregunta();
|
||||||
|
}
|
||||||
|
|
||||||
|
function mostrarPregunta() {
|
||||||
|
respondida = false;
|
||||||
|
btnSiguiente.style.display = 'none';
|
||||||
|
elFeedback.className = 'question-feedback';
|
||||||
|
elFeedback.textContent = '';
|
||||||
|
|
||||||
|
if (indice >= preguntas.length) {
|
||||||
|
if (faseActual === 'test' && Object.keys(supuestosData).length > 0) {
|
||||||
|
mostrarSelectorSupuesto();
|
||||||
|
} else {
|
||||||
|
finalizarExamen();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const p = preguntas[indice];
|
||||||
|
|
||||||
|
elPreguntaNum.textContent = `Pregunta ${indice + 1} de ${preguntas.length}`;
|
||||||
|
elPreguntaTxt.textContent = p.pregunta;
|
||||||
|
|
||||||
|
// Mostrar contexto si estamos en un supuesto práctico
|
||||||
|
if (faseActual === 'supuesto' && p.contexto?.descripcion) {
|
||||||
|
contextoPanel.style.display = 'block';
|
||||||
|
contextoPanel.innerHTML = `<i class="fas fa-info-circle"></i> <strong>Contexto:</strong> ${escHtml(p.contexto.descripcion)}`;
|
||||||
|
} else {
|
||||||
|
contextoPanel.style.display = 'none';
|
||||||
|
}
|
||||||
|
|
||||||
|
elOpciones.innerHTML = '';
|
||||||
|
for (const [letra, texto] of Object.entries(p.opciones)) {
|
||||||
|
const li = document.createElement('li');
|
||||||
|
li.className = 'options-list__item';
|
||||||
|
|
||||||
|
const label = document.createElement('label');
|
||||||
|
label.className = 'option-label';
|
||||||
|
label.innerHTML = `
|
||||||
|
<input type="radio" name="resp" value="${letra}">
|
||||||
|
<span class="option-letter">${letra.toUpperCase()})</span>
|
||||||
|
<span>${escHtml(texto)}</span>`;
|
||||||
|
|
||||||
|
label.querySelector('input').addEventListener('change', () => {
|
||||||
|
if (!respondida) comprobar(p);
|
||||||
|
});
|
||||||
|
|
||||||
|
li.appendChild(label);
|
||||||
|
elOpciones.appendChild(li);
|
||||||
|
}
|
||||||
|
|
||||||
|
actualizarMarcador();
|
||||||
|
}
|
||||||
|
|
||||||
|
function comprobar(p) {
|
||||||
|
respondida = true;
|
||||||
|
const marcada = document.querySelector('input[name="resp"]:checked');
|
||||||
|
if (!marcada) return;
|
||||||
|
|
||||||
|
// Deshabilitar todos los radio
|
||||||
|
document.querySelectorAll('input[name="resp"]').forEach(r => r.disabled = true);
|
||||||
|
|
||||||
|
// Marcar opciones
|
||||||
|
document.querySelectorAll('.option-label').forEach(label => {
|
||||||
|
const val = label.querySelector('input').value;
|
||||||
|
if (val === p.correcta) label.classList.add('correct');
|
||||||
|
else if (val === marcada.value) label.classList.add('incorrect');
|
||||||
|
});
|
||||||
|
|
||||||
|
if (marcada.value === p.correcta) {
|
||||||
|
aciertos++;
|
||||||
|
elFeedback.textContent = '✔ ¡Correcto!';
|
||||||
|
elFeedback.className = 'question-feedback show ok';
|
||||||
|
} else {
|
||||||
|
fallos++; preguntasFalladas.push({ pregunta: p, elegida: marcada.value }); elFeedback.textContent = `✘ Incorrecto. La respuesta correcta era la ${p.correcta.toUpperCase()})`;
|
||||||
|
elFeedback.className = 'question-feedback show ko';
|
||||||
|
}
|
||||||
|
|
||||||
|
actualizarMarcador();
|
||||||
|
btnSiguiente.style.display = 'inline-flex';
|
||||||
|
btnSiguiente.focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
function siguiente() {
|
||||||
|
indice++;
|
||||||
|
mostrarPregunta();
|
||||||
|
// mostrarPregunta() detecta si el índice superó el total y actúa
|
||||||
|
}
|
||||||
|
|
||||||
|
function actualizarMarcador() {
|
||||||
|
const contestadas = aciertos + fallos;
|
||||||
|
const puntosNetos = aciertos - fallos / 3;
|
||||||
|
const nota = contestadas > 0
|
||||||
|
? Math.max(0, (puntosNetos / preguntas.length) * 10).toFixed(2)
|
||||||
|
: '—';
|
||||||
|
|
||||||
|
elAciertos.textContent = aciertos;
|
||||||
|
elFallos.textContent = fallos;
|
||||||
|
elProgreso.textContent = `${contestadas} / ${preguntas.length}`;
|
||||||
|
elNota.textContent = nota;
|
||||||
|
}
|
||||||
|
|
||||||
|
function finalizarExamen() {
|
||||||
|
mostrarSolo('final');
|
||||||
|
|
||||||
|
const total = preguntas.length;
|
||||||
|
const puntosNetos = aciertos - fallos / 3;
|
||||||
|
const nota = Math.max(0, (puntosNetos / total) * 10).toFixed(2);
|
||||||
|
const sinRespuesta = total - aciertos - fallos;
|
||||||
|
|
||||||
|
document.getElementById('final-nota').textContent = nota;
|
||||||
|
document.getElementById('final-aciertos').textContent = aciertos;
|
||||||
|
document.getElementById('final-fallos').textContent = fallos;
|
||||||
|
document.getElementById('final-sin').textContent = sinRespuesta;
|
||||||
|
document.getElementById('final-total').textContent = total;
|
||||||
|
|
||||||
|
const notaNum = parseFloat(nota);
|
||||||
|
const color = notaNum >= 5 ? 'var(--success)' : notaNum >= 4 ? 'var(--warning)' : 'var(--error)';
|
||||||
|
document.getElementById('final-nota').style.color = color;
|
||||||
|
|
||||||
|
renderRepaso();
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderRepaso() {
|
||||||
|
const wrap = document.getElementById('repaso-wrap');
|
||||||
|
if (!wrap) return;
|
||||||
|
|
||||||
|
if (preguntasFalladas.length === 0) {
|
||||||
|
wrap.innerHTML = '<p class="repaso-perfecto"><i class="fas fa-star"></i> ¡Sin fallos! Dominas todo el temario de este examen.</p>';
|
||||||
|
wrap.style.display = 'block';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Agrupar fallos por tema detectado
|
||||||
|
const grupos = {};
|
||||||
|
for (const { pregunta, elegida } of preguntasFalladas) {
|
||||||
|
const tema = detectarTema(pregunta.pregunta);
|
||||||
|
if (!grupos[tema.id]) grupos[tema.id] = { tema, items: [] };
|
||||||
|
grupos[tema.id].items.push({ pregunta, elegida });
|
||||||
|
}
|
||||||
|
|
||||||
|
const n = preguntasFalladas.length;
|
||||||
|
wrap.innerHTML = `
|
||||||
|
<h3 class="repaso-titulo"><i class="fas fa-exclamation-triangle"></i> Necesitas repasar</h3>
|
||||||
|
<p class="repaso-intro">Has fallado <strong>${n}</strong> pregunta${n > 1 ? 's' : ''}. Estos son los temas donde debes reforzar:</p>
|
||||||
|
${Object.values(grupos).map(g => `
|
||||||
|
<details class="repaso-grupo" open>
|
||||||
|
<summary class="repaso-tema">
|
||||||
|
<span class="repaso-tema-name"><i class="fas fa-book"></i> ${escHtml(g.tema.label)}</span>
|
||||||
|
<span class="repaso-badge">${g.items.length} fallo${g.items.length > 1 ? 's' : ''}</span>
|
||||||
|
</summary>
|
||||||
|
<ul class="repaso-lista">
|
||||||
|
${g.items.map(({ pregunta: p, elegida }) => `
|
||||||
|
<li class="repaso-item">
|
||||||
|
<p class="repaso-q">${escHtml(p.pregunta.length > 140 ? p.pregunta.slice(0,140)+'…' : p.pregunta)}</p>
|
||||||
|
<div class="repaso-answers">
|
||||||
|
<span class="repaso-ans ko"><i class="fas fa-times"></i> Tu resp.: ${elegida.toUpperCase()}) ${escHtml((p.opciones[elegida]||'').length > 70 ? p.opciones[elegida].slice(0,70)+'…' : (p.opciones[elegida]||''))}</span>
|
||||||
|
<span class="repaso-ans ok"><i class="fas fa-check"></i> Correcta: ${p.correcta.toUpperCase()}) ${escHtml((p.opciones[p.correcta]||'').length > 70 ? p.opciones[p.correcta].slice(0,70)+'…' : (p.opciones[p.correcta]||''))}</span>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
`).join('')}
|
||||||
|
</ul>
|
||||||
|
<a href="${g.tema.link}" class="repaso-link-tema"><i class="fas fa-book-open"></i> Estudiar este tema</a>
|
||||||
|
</details>
|
||||||
|
`).join('')}
|
||||||
|
`;
|
||||||
|
wrap.style.display = 'block';
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Helpers ───────────────────────────────────────────────────
|
||||||
|
function mezclar(arr) {
|
||||||
|
const a = [...arr];
|
||||||
|
for (let i = a.length - 1; i > 0; i--) {
|
||||||
|
const j = Math.floor(Math.random() * (i + 1));
|
||||||
|
[a[i], a[j]] = [a[j], a[i]];
|
||||||
|
}
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
function escHtml(str) {
|
||||||
|
return str
|
||||||
|
.replace(/&/g, '&')
|
||||||
|
.replace(/</g, '<')
|
||||||
|
.replace(/>/g, '>')
|
||||||
|
.replace(/"/g, '"');
|
||||||
|
}
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -0,0 +1,54 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="es">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>TAI – AGE | Temario</title>
|
||||||
|
<link rel="stylesheet" href="css/style.css">
|
||||||
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.0/css/all.min.css">
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;700&display=swap" rel="stylesheet">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<!-- ── Topbar ─────────────────────────────────────────── -->
|
||||||
|
<nav class="topbar">
|
||||||
|
<button class="menu-toggle" id="menu-toggle" aria-label="Menú">
|
||||||
|
<i class="fas fa-bars"></i>
|
||||||
|
</button>
|
||||||
|
<a href="index.html" class="topbar-brand" style="text-decoration:none">
|
||||||
|
<i class="fas fa-graduation-cap"></i> TAI – AGE
|
||||||
|
</a>
|
||||||
|
<nav class="topbar-nav">
|
||||||
|
<a href="index.html">Inicio</a>
|
||||||
|
<a href="curso.html" class="active">Temario</a>
|
||||||
|
<a href="cuestionarios/index.html">Cuestionarios</a>
|
||||||
|
<a href="noticias.html"><i class="fas fa-bell"></i> Noticias</a>
|
||||||
|
</nav>
|
||||||
|
<span class="topbar-progress" id="topbar-progress">1 / 33</span>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<!-- ── Layout ─────────────────────────────────────────── -->
|
||||||
|
<div class="player-layout">
|
||||||
|
|
||||||
|
<!-- Sidebar -->
|
||||||
|
<aside class="sidebar" id="sidebar">
|
||||||
|
<!-- Rellena js/curso.js -->
|
||||||
|
</aside>
|
||||||
|
|
||||||
|
<!-- Content -->
|
||||||
|
<main class="content-pane">
|
||||||
|
<div id="lesson-content">
|
||||||
|
<div class="spinner"><i class="fas fa-circle-notch fa-spin"></i></div>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ── Scripts ────────────────────────────────────────── -->
|
||||||
|
<!-- marked.js para renderizar Markdown en el cliente -->
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/marked@12/marked.min.js"></script>
|
||||||
|
<script src="js/temas.js"></script>
|
||||||
|
<script src="js/curso.js"></script>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
@ -0,0 +1,194 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="es">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>TAI – AGE | Temario oposición</title>
|
||||||
|
<link rel="stylesheet" href="css/style.css">
|
||||||
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.0/css/all.min.css">
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;700&display=swap" rel="stylesheet">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<!-- ── Topbar ─────────────────────────────────────────── -->
|
||||||
|
<nav class="topbar">
|
||||||
|
<span class="topbar-brand"><i class="fas fa-graduation-cap"></i> TAI – AGE</span>
|
||||||
|
<nav class="topbar-nav">
|
||||||
|
<a href="index.html" class="active">Inicio</a>
|
||||||
|
<a href="curso.html">Temario</a>
|
||||||
|
<a href="cuestionarios/index.html">Cuestionarios</a>
|
||||||
|
<a href="noticias.html">Noticias</a>
|
||||||
|
</nav>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<!-- ── Banner cambios INAP ──────────────────────────────── -->
|
||||||
|
<div id="inap-cambio-banner" class="inap-cambio-banner" style="display:none">
|
||||||
|
<div class="inap-cambio-inner">
|
||||||
|
<i class="fas fa-exclamation-circle"></i>
|
||||||
|
<div class="inap-cambio-txt">
|
||||||
|
<strong>¡La página de la convocatoria TAI ha cambiado!</strong>
|
||||||
|
<span>El INAP ha actualizado la información del proceso selectivo.</span>
|
||||||
|
</div>
|
||||||
|
<a class="inap-banner-link btn btn-primary" href="#" target="_blank" rel="noopener" style="white-space:nowrap">
|
||||||
|
<i class="fas fa-external-link-alt"></i> Ver cambios
|
||||||
|
</a>
|
||||||
|
<button class="inap-banner-close" onclick="cerrarBannerInap()" aria-label="Cerrar">
|
||||||
|
<i class="fas fa-times"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ── Hero ──────────────────────────────────────────── -->
|
||||||
|
<section class="home-hero">
|
||||||
|
<h1><i class="fas fa-laptop-code"></i> Temario TAI – AGE</h1>
|
||||||
|
<p>Técnico/a en Informática – Administración General del Estado.<br>
|
||||||
|
Todos los bloques, todos los temas. Sin filtros.</p>
|
||||||
|
|
||||||
|
<div class="home-stats">
|
||||||
|
<div class="stat-item">
|
||||||
|
<div class="stat-num">4</div>
|
||||||
|
<div class="stat-lbl">Bloques</div>
|
||||||
|
</div>
|
||||||
|
<div class="stat-item">
|
||||||
|
<div class="stat-num">33</div>
|
||||||
|
<div class="stat-lbl">Temas</div>
|
||||||
|
</div>
|
||||||
|
<div class="stat-item">
|
||||||
|
<div class="stat-num">4</div>
|
||||||
|
<div class="stat-lbl">Exámenes reales</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style="display:flex;gap:1rem;justify-content:center;flex-wrap:wrap;">
|
||||||
|
<a href="curso.html" class="btn btn-primary"><i class="fas fa-play"></i> Empezar el temario</a>
|
||||||
|
<a href="cuestionarios/index.html" class="btn btn-outline"><i class="fas fa-question-circle"></i> Practicar exámenes</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Cuenta atrás -->
|
||||||
|
<div class="countdown-wrap">
|
||||||
|
<div class="countdown-label"><i class="fas fa-calendar-alt"></i> Examen TAI · 23 de mayo de 2026</div>
|
||||||
|
<div class="countdown-units" id="countdown">
|
||||||
|
<div class="cd-unit"><span class="cd-num" id="cd-days">--</span><span class="cd-lbl">días</span></div>
|
||||||
|
<div class="cd-sep">:</div>
|
||||||
|
<div class="cd-unit"><span class="cd-num" id="cd-hours">--</span><span class="cd-lbl">horas</span></div>
|
||||||
|
<div class="cd-sep">:</div>
|
||||||
|
<div class="cd-unit"><span class="cd-num" id="cd-mins">--</span><span class="cd-lbl">min</span></div>
|
||||||
|
<div class="cd-sep">:</div>
|
||||||
|
<div class="cd-unit"><span class="cd-num" id="cd-secs">--</span><span class="cd-lbl">seg</span></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<script src="js/inap-watch.js"></script>
|
||||||
|
<script>
|
||||||
|
(function () {
|
||||||
|
const target = new Date('2026-05-23T00:00:00');
|
||||||
|
function tick() {
|
||||||
|
const diff = target - Date.now();
|
||||||
|
if (diff <= 0) {
|
||||||
|
document.getElementById('countdown').innerHTML = '<span style="color:var(--accent);font-size:1.4rem">¡Hoy es el día! 🎯</span>';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const d = Math.floor(diff / 86400000);
|
||||||
|
const h = Math.floor((diff % 86400000) / 3600000);
|
||||||
|
const m = Math.floor((diff % 3600000) / 60000);
|
||||||
|
const s = Math.floor((diff % 60000) / 1000);
|
||||||
|
document.getElementById('cd-days').textContent = String(d).padStart(2, '0');
|
||||||
|
document.getElementById('cd-hours').textContent = String(h).padStart(2, '0');
|
||||||
|
document.getElementById('cd-mins').textContent = String(m).padStart(2, '0');
|
||||||
|
document.getElementById('cd-secs').textContent = String(s).padStart(2, '0');
|
||||||
|
}
|
||||||
|
tick();
|
||||||
|
setInterval(tick, 1000);
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<!-- ── Bloques ────────────────────────────────────────── -->
|
||||||
|
<section class="bloques-section">
|
||||||
|
<h2>Bloques del temario</h2>
|
||||||
|
<div class="bloques-grid">
|
||||||
|
|
||||||
|
<article class="bloque-card" onclick="location.href='curso.html?bloque=1&tema=1'">
|
||||||
|
<div class="bloque-num">Bloque I</div>
|
||||||
|
<h3>Organización del Estado y Administración electrónica</h3>
|
||||||
|
<p>Constitución, EBEP, igualdad, identidad digital, protección de datos, sede electrónica.</p>
|
||||||
|
<ul class="bloque-temas-list">
|
||||||
|
<li>T1 – Constitución Española de 1978</li>
|
||||||
|
<li>T2 – Cortes Generales y Tribunal Constitucional</li>
|
||||||
|
<li>T3 – El Gobierno</li>
|
||||||
|
<li>T4 – EBEP, transparencia y ODS</li>
|
||||||
|
<li>T5 – Igualdad y discapacidad</li>
|
||||||
|
<li>T6 – Sociedad de la información e identidad digital</li>
|
||||||
|
<li>T7 – Protección de datos</li>
|
||||||
|
<li>T8 – Acceso electrónico a los servicios públicos</li>
|
||||||
|
<li>T9 – Sedes e infraestructuras electrónicas</li>
|
||||||
|
</ul>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
<article class="bloque-card" onclick="location.href='curso.html?bloque=2&tema=1'">
|
||||||
|
<div class="bloque-num">Bloque II</div>
|
||||||
|
<h3>Tecnología básica</h3>
|
||||||
|
<p>Hardware, periféricos, estructuras de datos, sistemas operativos, BBDD.</p>
|
||||||
|
<ul class="bloque-temas-list">
|
||||||
|
<li>T1 – Informática básica y arquitectura</li>
|
||||||
|
<li>T2 – Periféricos y almacenamiento</li>
|
||||||
|
<li>T3 – Tipos de datos y algoritmos</li>
|
||||||
|
<li>T4 – Sistemas operativos</li>
|
||||||
|
<li>T5 – Sistemas gestores de BBDD</li>
|
||||||
|
</ul>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
<article class="bloque-card" onclick="location.href='curso.html?bloque=3&tema=1'">
|
||||||
|
<div class="bloque-num">Bloque III</div>
|
||||||
|
<h3>Desarrollo de sistemas</h3>
|
||||||
|
<p>Modelado de datos, SQL, POO, Java EE, web, UML, pruebas y control de versiones.</p>
|
||||||
|
<ul class="bloque-temas-list">
|
||||||
|
<li>T1 – Modelado y diseño de BBDD</li>
|
||||||
|
<li>T2 – Lenguajes de programación</li>
|
||||||
|
<li>T3 – SQL y procedimientos almacenados</li>
|
||||||
|
<li>T4 – POO y patrones de diseño</li>
|
||||||
|
<li>T5 – Java EE / .NET</li>
|
||||||
|
<li>T6 – Arquitecturas cliente/servidor y web services</li>
|
||||||
|
<li>T7 – Desarrollo web front-end y back-end</li>
|
||||||
|
<li>T8 – Accesibilidad y seguridad en desarrollo</li>
|
||||||
|
<li>T9 – Repositorios y metodologías</li>
|
||||||
|
</ul>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
<article class="bloque-card" onclick="location.href='curso.html?bloque=4&tema=1'">
|
||||||
|
<div class="bloque-num">Bloque IV</div>
|
||||||
|
<h3>Sistemas y comunicaciones</h3>
|
||||||
|
<p>Administración de SO, BBDD, correo, redes, seguridad, TCP/IP, Internet, VPN.</p>
|
||||||
|
<ul class="bloque-temas-list">
|
||||||
|
<li>T1 – Administración del SO</li>
|
||||||
|
<li>T2 – Administración de BBDD y virtualización</li>
|
||||||
|
<li>T3 – Servidores de correo y contenedores</li>
|
||||||
|
<li>T4 – Administración de redes de área local</li>
|
||||||
|
<li>T5 – Seguridad de sistemas</li>
|
||||||
|
<li>T6 – Comunicaciones y redes</li>
|
||||||
|
<li>T7 – Modelos TCP/IP y OSI</li>
|
||||||
|
<li>T8 – Internet y HTTP/S</li>
|
||||||
|
<li>T9 – Seguridad perimetral y VPN</li>
|
||||||
|
<li>T10 – Redes locales</li>
|
||||||
|
</ul>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- ── Cuestionarios banner ───────────────────────────── -->
|
||||||
|
<section class="quiz-card">
|
||||||
|
<div class="quiz-banner">
|
||||||
|
<div class="quiz-banner-icon"><i class="fas fa-file-alt" style="color:var(--accent)"></i></div>
|
||||||
|
<div>
|
||||||
|
<h3>Exámenes reales TAI (2019 – 2024)</h3>
|
||||||
|
<p>Practica con preguntas de convocatorias anteriores: TAI 2019, TAI 2023, TAI 2024 A y B.</p>
|
||||||
|
</div>
|
||||||
|
<a href="cuestionarios/index.html" class="btn btn-primary">
|
||||||
|
<i class="fas fa-play"></i> Practicar ahora
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
@ -0,0 +1,202 @@
|
||||||
|
/**
|
||||||
|
* js/curso.js
|
||||||
|
* Lógica del visor de curso: sidebar, carga de markdown y navegación.
|
||||||
|
* Depende de: js/temas.js + marked.js (CDN)
|
||||||
|
*/
|
||||||
|
|
||||||
|
// ── Estado ──────────────────────────────────────────────────
|
||||||
|
let currentBloque = 1;
|
||||||
|
let currentTema = 1;
|
||||||
|
|
||||||
|
// ── Init ────────────────────────────────────────────────────
|
||||||
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
const params = new URLSearchParams(location.search);
|
||||||
|
currentBloque = parseInt(params.get('bloque') || '1', 10);
|
||||||
|
currentTema = parseInt(params.get('tema') || '1', 10);
|
||||||
|
|
||||||
|
buildSidebar();
|
||||||
|
loadTema(currentBloque, currentTema);
|
||||||
|
});
|
||||||
|
|
||||||
|
// ── Build sidebar ───────────────────────────────────────────
|
||||||
|
function buildSidebar() {
|
||||||
|
const sidebar = document.querySelector('.sidebar');
|
||||||
|
if (!sidebar) return;
|
||||||
|
|
||||||
|
let html = '<div class="sidebar-header">TEMARIO TAI – AGE</div>';
|
||||||
|
|
||||||
|
for (const bloque of TEMARIO) {
|
||||||
|
const isOpenBloque = (bloque.id === currentBloque);
|
||||||
|
html += `
|
||||||
|
<div class="bloque-group ${isOpenBloque ? 'open' : ''}" id="bloque-group-${bloque.id}">
|
||||||
|
<div class="bloque-group-header" onclick="toggleBloque(${bloque.id})">
|
||||||
|
<span style="color:${bloque.color}">■</span>
|
||||||
|
<span>Bloque ${toRoman(bloque.id)}</span>
|
||||||
|
<span class="chevron">▶</span>
|
||||||
|
</div>
|
||||||
|
<div class="bloque-group-items">`;
|
||||||
|
|
||||||
|
for (const tema of bloque.temas) {
|
||||||
|
const isActive = (bloque.id === currentBloque && tema.num === currentTema);
|
||||||
|
const shortTitle = shortTemaTitle(tema.titulo);
|
||||||
|
html += `
|
||||||
|
<div class="sidebar-item ${isActive ? 'active' : ''}"
|
||||||
|
id="item-${bloque.id}-${tema.num}"
|
||||||
|
onclick="navigateTo(${bloque.id}, ${tema.num})">
|
||||||
|
<span class="item-num">${tema.num}.</span>
|
||||||
|
<span>${shortTitle}</span>
|
||||||
|
</div>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
html += `
|
||||||
|
</div>
|
||||||
|
</div>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
sidebar.innerHTML = html;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Toggle bloque ────────────────────────────────────────────
|
||||||
|
function toggleBloque(id) {
|
||||||
|
const group = document.getElementById(`bloque-group-${id}`);
|
||||||
|
if (group) group.classList.toggle('open');
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Navigate ─────────────────────────────────────────────────
|
||||||
|
function navigateTo(bloqueId, temaNum) {
|
||||||
|
currentBloque = bloqueId;
|
||||||
|
currentTema = temaNum;
|
||||||
|
const url = new URL(location.href);
|
||||||
|
url.searchParams.set('bloque', bloqueId);
|
||||||
|
url.searchParams.set('tema', temaNum);
|
||||||
|
history.pushState({}, '', url.toString());
|
||||||
|
updateSidebarActive();
|
||||||
|
loadTema(bloqueId, temaNum);
|
||||||
|
// Cerrar sidebar en móvil
|
||||||
|
document.querySelector('.sidebar')?.classList.remove('open');
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Load markdown ─────────────────────────────────────────────
|
||||||
|
async function loadTema(bloqueId, temaNum) {
|
||||||
|
const bloque = TEMARIO.find(b => b.id === bloqueId);
|
||||||
|
if (!bloque) return showError('Bloque no encontrado.');
|
||||||
|
const tema = bloque.temas.find(t => t.num === temaNum);
|
||||||
|
if (!tema) return showError('Tema no encontrado.');
|
||||||
|
|
||||||
|
const content = document.getElementById('lesson-content');
|
||||||
|
if (!content) return;
|
||||||
|
|
||||||
|
content.innerHTML = '<div class="spinner"><i class="fas fa-circle-notch fa-spin"></i></div>';
|
||||||
|
|
||||||
|
try {
|
||||||
|
const resp = await fetch(tema.archivo);
|
||||||
|
if (!resp.ok) throw new Error(`HTTP ${resp.status}`);
|
||||||
|
const md = await resp.text();
|
||||||
|
renderMarkdown(md, tema, bloque, bloqueId, temaNum);
|
||||||
|
} catch (e) {
|
||||||
|
showError(`No se ha podido cargar <strong>${tema.archivo}</strong>.<br><small>${e.message}</small>`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderMarkdown(md, tema, bloque, bloqueId, temaNum) {
|
||||||
|
const { prev, next, pos, total } = getNavigation(bloqueId, temaNum);
|
||||||
|
|
||||||
|
const prevBtn = prev
|
||||||
|
? `<button class="lesson-nav-btn" onclick="navigateTo(${prev.bloqueId},${prev.temaNum})">
|
||||||
|
<i class="fas fa-chevron-left"></i> Anterior
|
||||||
|
</button>`
|
||||||
|
: `<button class="lesson-nav-btn disabled"><i class="fas fa-chevron-left"></i> Anterior</button>`;
|
||||||
|
|
||||||
|
const nextBtn = next
|
||||||
|
? `<button class="lesson-nav-btn" onclick="navigateTo(${next.bloqueId},${next.temaNum})">
|
||||||
|
Siguiente <i class="fas fa-chevron-right"></i>
|
||||||
|
</button>`
|
||||||
|
: `<button class="lesson-nav-btn disabled">Siguiente <i class="fas fa-chevron-right"></i></button>`;
|
||||||
|
|
||||||
|
const html = marked.parse(md);
|
||||||
|
|
||||||
|
document.getElementById('lesson-content').innerHTML = `
|
||||||
|
<div class="md-body">${html}</div>
|
||||||
|
<nav class="lesson-nav" aria-label="Navegación entre temas">
|
||||||
|
${prevBtn}
|
||||||
|
<span class="lesson-pos">Tema ${pos} de ${total} · Bloque ${toRoman(bloqueId)}</span>
|
||||||
|
${nextBtn}
|
||||||
|
</nav>`;
|
||||||
|
|
||||||
|
// Actualizar título de pestaña
|
||||||
|
document.title = `T${temaNum} – Bloque ${toRoman(bloqueId)} | TAI–AGE`;
|
||||||
|
|
||||||
|
// Scroll arriba
|
||||||
|
document.querySelector('.content-pane')?.scrollTo(0, 0);
|
||||||
|
|
||||||
|
// Actualizar progress en topbar
|
||||||
|
updateTopbarProgress(pos, total);
|
||||||
|
}
|
||||||
|
|
||||||
|
function showError(msg) {
|
||||||
|
document.getElementById('lesson-content').innerHTML = `
|
||||||
|
<div class="empty-state">
|
||||||
|
<i class="fas fa-exclamation-triangle" style="color:var(--warning)"></i>
|
||||||
|
<p>Error cargando el tema</p>
|
||||||
|
<p style="font-size:.85rem;color:var(--text-muted)">${msg}</p>
|
||||||
|
</div>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Sidebar active ────────────────────────────────────────────
|
||||||
|
function updateSidebarActive() {
|
||||||
|
document.querySelectorAll('.sidebar-item').forEach(el => el.classList.remove('active'));
|
||||||
|
const el = document.getElementById(`item-${currentBloque}-${currentTema}`);
|
||||||
|
if (el) {
|
||||||
|
el.classList.add('active');
|
||||||
|
el.scrollIntoView({ block: 'nearest' });
|
||||||
|
}
|
||||||
|
// Abrir bloque activo
|
||||||
|
document.querySelectorAll('.bloque-group').forEach(g => g.classList.remove('open'));
|
||||||
|
document.getElementById(`bloque-group-${currentBloque}`)?.classList.add('open');
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Topbar progress ───────────────────────────────────────────
|
||||||
|
function updateTopbarProgress(pos, total) {
|
||||||
|
const el = document.querySelector('.topbar-progress');
|
||||||
|
if (el) el.textContent = `${pos} / ${total}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Hamburger (móvil) ─────────────────────────────────────────
|
||||||
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
const toggle = document.getElementById('menu-toggle');
|
||||||
|
if (toggle) {
|
||||||
|
toggle.addEventListener('click', () => {
|
||||||
|
document.querySelector('.sidebar').classList.toggle('open');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// Cerrar sidebar al pulsar fuera (móvil)
|
||||||
|
document.addEventListener('click', e => {
|
||||||
|
if (window.innerWidth > 768) return;
|
||||||
|
const sidebar = document.querySelector('.sidebar');
|
||||||
|
if (sidebar && !sidebar.contains(e.target) && e.target.id !== 'menu-toggle') {
|
||||||
|
sidebar.classList.remove('open');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// ── broswer back/forward ──────────────────────────────────────
|
||||||
|
window.addEventListener('popstate', () => {
|
||||||
|
const params = new URLSearchParams(location.search);
|
||||||
|
currentBloque = parseInt(params.get('bloque') || '1', 10);
|
||||||
|
currentTema = parseInt(params.get('tema') || '1', 10);
|
||||||
|
updateSidebarActive();
|
||||||
|
loadTema(currentBloque, currentTema);
|
||||||
|
});
|
||||||
|
|
||||||
|
// ── Helpers ───────────────────────────────────────────────────
|
||||||
|
function toRoman(n) {
|
||||||
|
return ['', 'I', 'II', 'III', 'IV', 'V'][n] ?? n;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Acorta el título del tema al primer fragmento antes de "." o ":" (máx 55 chars).
|
||||||
|
*/
|
||||||
|
function shortTemaTitle(titulo) {
|
||||||
|
const first = titulo.split(/[.:–]/)[0].trim();
|
||||||
|
return first.length > 55 ? first.slice(0, 53) + '…' : first;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,106 @@
|
||||||
|
'use strict';
|
||||||
|
/**
|
||||||
|
* inap-watch.js
|
||||||
|
* Comprueba si la página de la convocatoria TAI del INAP ha cambiado
|
||||||
|
* desde la última visita. Si detecta cambios, muestra un banner en la
|
||||||
|
* página de inicio.
|
||||||
|
*
|
||||||
|
* Estrategia:
|
||||||
|
* 1. Descarga la página via un proxy CORS (allorigins.win)
|
||||||
|
* 2. Extrae solo el texto visible (sin scripts/estilos) y normaliza espacios
|
||||||
|
* 3. Calcula un hash djb2 del texto
|
||||||
|
* 4. Compara con el hash almacenado en localStorage
|
||||||
|
* 5. Si difiere → muestra el banner y guarda el nuevo hash
|
||||||
|
* (el usuario puede silenciarlo con "Lo he visto")
|
||||||
|
*/
|
||||||
|
|
||||||
|
const INAP_URL = 'https://sede.inap.gob.es/es/procedimientos-y-servicios/seleccion/procesos-selectivos-de-cuerpos-y-escalas-generales/cuerpo-de-tecnicos-auxiliares-de-informatica-de-la-administracion-del-estado-ingreso-libre-convocatoria-2025';
|
||||||
|
const PROXY = 'https://api.allorigins.win/get?url=';
|
||||||
|
const STORE_KEY = 'inap_tai_hash';
|
||||||
|
const SEEN_KEY = 'inap_tai_hash_seen';
|
||||||
|
|
||||||
|
function djb2(str) {
|
||||||
|
let h = 5381;
|
||||||
|
for (let i = 0; i < str.length; i++) {
|
||||||
|
h = ((h << 5) + h) ^ str.charCodeAt(i);
|
||||||
|
h = h >>> 0; // mantener 32 bits sin signo
|
||||||
|
}
|
||||||
|
return h.toString(16);
|
||||||
|
}
|
||||||
|
|
||||||
|
function extraerTexto(html) {
|
||||||
|
// Eliminar scripts, estilos, comentarios y atributos dinámicos
|
||||||
|
return html
|
||||||
|
.replace(/<script[\s\S]*?<\/script>/gi, '')
|
||||||
|
.replace(/<style[\s\S]*?<\/style>/gi, '')
|
||||||
|
.replace(/<!--[\s\S]*?-->/g, '')
|
||||||
|
.replace(/<[^>]+>/g, ' ') // quitar etiquetas
|
||||||
|
.replace(/\s+/g, ' ') // normalizar espacios
|
||||||
|
.trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
function mostrarBanner(urlPagina) {
|
||||||
|
const banner = document.getElementById('inap-cambio-banner');
|
||||||
|
if (!banner) return;
|
||||||
|
banner.style.display = 'flex';
|
||||||
|
banner.querySelector('.inap-banner-link').href = urlPagina;
|
||||||
|
}
|
||||||
|
|
||||||
|
function ocultarBanner() {
|
||||||
|
const banner = document.getElementById('inap-cambio-banner');
|
||||||
|
if (!banner) return;
|
||||||
|
banner.style.display = 'none';
|
||||||
|
// Marcar como visto para esta versión del hash
|
||||||
|
const hash = localStorage.getItem(STORE_KEY);
|
||||||
|
if (hash) localStorage.setItem(SEEN_KEY, hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function comprobarCambios() {
|
||||||
|
// Si el navegador no tiene fetch o localStorage, salir silenciosamente
|
||||||
|
if (typeof fetch === 'undefined' || typeof localStorage === 'undefined') return;
|
||||||
|
|
||||||
|
const hashPrevio = localStorage.getItem(STORE_KEY);
|
||||||
|
const hashVisto = localStorage.getItem(SEEN_KEY);
|
||||||
|
|
||||||
|
// Si ya hay cambio pendiente que el usuario aún no ha marcado como visto → mostrar banner
|
||||||
|
if (hashPrevio && hashPrevio !== hashVisto) {
|
||||||
|
mostrarBanner(INAP_URL);
|
||||||
|
// Seguir para comprobar si hay cambios adicionales desde entonces
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const resp = await fetch(PROXY + encodeURIComponent(INAP_URL), { cache: 'no-store' });
|
||||||
|
if (!resp.ok) return;
|
||||||
|
const data = await resp.json();
|
||||||
|
const texto = extraerTexto(data.contents || '');
|
||||||
|
if (!texto) return;
|
||||||
|
|
||||||
|
const hashNuevo = djb2(texto);
|
||||||
|
|
||||||
|
if (!hashPrevio) {
|
||||||
|
// Primera visita: guardar referencia silenciosamente
|
||||||
|
localStorage.setItem(STORE_KEY, hashNuevo);
|
||||||
|
localStorage.setItem(SEEN_KEY, hashNuevo);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hashNuevo !== hashPrevio) {
|
||||||
|
// La página cambió
|
||||||
|
localStorage.setItem(STORE_KEY, hashNuevo);
|
||||||
|
mostrarBanner(INAP_URL);
|
||||||
|
}
|
||||||
|
} catch (_) {
|
||||||
|
// Fallo de red o proxy caído: no hacer nada, sin ruido
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Arranque ───────────────────────────────────────────────────
|
||||||
|
// Exponer función de cierre al botón del banner
|
||||||
|
window.cerrarBannerInap = ocultarBanner;
|
||||||
|
|
||||||
|
// Ejecutar cuando la página esté lista (sin bloquear el render)
|
||||||
|
if (document.readyState === 'loading') {
|
||||||
|
document.addEventListener('DOMContentLoaded', comprobarCambios);
|
||||||
|
} else {
|
||||||
|
comprobarCambios();
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,84 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
// ── Configuración ──────────────────────────────────────────────
|
||||||
|
// BOE Sección II-B: Oposiciones, concursos y vacantes
|
||||||
|
const BOE_RSS = 'https://www.boe.es/rss/boe_dias.php?s=2B';
|
||||||
|
// Proxy CORS gratuito que convierte RSS a JSON
|
||||||
|
const RSS2JSON = 'https://api.rss2json.com/v1/api.json?rss_url=';
|
||||||
|
// Palabras clave para resaltar entradas de interés informático/TAI
|
||||||
|
const KW_TAI = /\b(TAI|T\.A\.I|técnico[a]? (de )?administración (general |de la )?inform|inform[aá]tic|sistemas?\s+de\s+información|AGE\b)/i;
|
||||||
|
|
||||||
|
// ── Carga del feed BOE ─────────────────────────────────────────
|
||||||
|
async function cargarBOE() {
|
||||||
|
const panel = document.getElementById('boe-items');
|
||||||
|
const badge = document.getElementById('boe-fecha');
|
||||||
|
|
||||||
|
try {
|
||||||
|
const resp = await fetch(RSS2JSON + encodeURIComponent(BOE_RSS));
|
||||||
|
if (!resp.ok) throw new Error('HTTP ' + resp.status);
|
||||||
|
const data = await resp.json();
|
||||||
|
if (data.status !== 'ok') throw new Error('Feed error: ' + data.message);
|
||||||
|
|
||||||
|
const items = data.items || [];
|
||||||
|
if (items.length === 0) {
|
||||||
|
panel.innerHTML = '<p class="news-empty">No hay publicaciones recientes en esta sección.</p>';
|
||||||
|
badge.textContent = 'Sin datos';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Separar: primero los que coincidan con TAI/informática, luego el resto
|
||||||
|
const importantes = items.filter(it => KW_TAI.test(it.title + ' ' + (it.description || '')));
|
||||||
|
const resto = items.filter(it => !KW_TAI.test(it.title + ' ' + (it.description || '')));
|
||||||
|
const ordenados = [...importantes, ...resto].slice(0, 25);
|
||||||
|
|
||||||
|
// Fecha de la última actualización del feed
|
||||||
|
const fechaFeed = data.feed?.lastBuildDate
|
||||||
|
? new Date(data.feed.lastBuildDate).toLocaleDateString('es-ES', { day:'2-digit', month:'short', year:'numeric' })
|
||||||
|
: 'hoy';
|
||||||
|
badge.textContent = 'Act. ' + fechaFeed;
|
||||||
|
|
||||||
|
panel.innerHTML = ordenados.map(item => {
|
||||||
|
const esTAI = KW_TAI.test(item.title + ' ' + (item.description || ''));
|
||||||
|
const fecha = item.pubDate
|
||||||
|
? new Date(item.pubDate).toLocaleDateString('es-ES', { day:'2-digit', month:'short', year:'numeric' })
|
||||||
|
: '';
|
||||||
|
const titulo = escHtml(truncar(item.title || 'Sin título', 120));
|
||||||
|
|
||||||
|
return `
|
||||||
|
<a href="${escHtml(item.link || '#')}" target="_blank" rel="noopener"
|
||||||
|
class="news-item${esTAI ? ' news-item--tai' : ''}">
|
||||||
|
<div class="news-item-meta">
|
||||||
|
${esTAI
|
||||||
|
? '<span class="news-tag-tai"><i class="fas fa-star"></i> TAI / Informática</span>'
|
||||||
|
: ''}
|
||||||
|
<span class="news-date">${fecha}</span>
|
||||||
|
</div>
|
||||||
|
<div class="news-title">${titulo}</div>
|
||||||
|
</a>`;
|
||||||
|
}).join('');
|
||||||
|
|
||||||
|
} catch (err) {
|
||||||
|
console.warn('[noticias.js] Error cargando BOE:', err.message);
|
||||||
|
badge.textContent = 'Error';
|
||||||
|
panel.innerHTML = `
|
||||||
|
<div class="news-error">
|
||||||
|
<i class="fas fa-exclamation-triangle"></i>
|
||||||
|
No se pudo cargar el feed automáticamente. Consulta el BOE directamente:
|
||||||
|
<a href="https://www.boe.es/diario_boe/ultimos_dias.php" target="_blank" rel="noopener">
|
||||||
|
Últimas publicaciones BOE <i class="fas fa-external-link-alt"></i>
|
||||||
|
</a>
|
||||||
|
</div>`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Helpers ────────────────────────────────────────────────────
|
||||||
|
function escHtml(s) {
|
||||||
|
return (s || '').replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"');
|
||||||
|
}
|
||||||
|
|
||||||
|
function truncar(s, max) {
|
||||||
|
return s.length > max ? s.slice(0, max) + '…' : s;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Arranque ───────────────────────────────────────────────────
|
||||||
|
cargarBOE();
|
||||||
|
|
@ -0,0 +1,226 @@
|
||||||
|
/**
|
||||||
|
* js/temas.js
|
||||||
|
* Mapa completo del temario TAI – AGE.
|
||||||
|
* Cada bloque incluye todos los temas del README.md exactamente.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const TEMARIO = [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
titulo: "Organización del Estado y Administración electrónica",
|
||||||
|
color: "#007acc",
|
||||||
|
temas: [
|
||||||
|
{
|
||||||
|
num: 1,
|
||||||
|
titulo: "La Constitución Española de 1978. Derechos y deberes fundamentales. Su garantía y suspensión. La Corona: funciones constitucionales del Rey.",
|
||||||
|
archivo: "bloque1/tema1.md"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
num: 2,
|
||||||
|
titulo: "Las Cortes Generales: atribuciones del Congreso de los Diputados y del Senado. El Tribunal Constitucional: composición y atribuciones. El Defensor del Pueblo.",
|
||||||
|
archivo: "bloque1/tema2.md"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
num: 3,
|
||||||
|
titulo: "El Gobierno: composición, nombramiento y cese. Las funciones del Gobierno. Relaciones entre el Gobierno y las Cortes Generales.",
|
||||||
|
archivo: "bloque1/tema3.md"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
num: 4,
|
||||||
|
titulo: "Estatuto Básico del Empleado Público: derechos y deberes, provisión de puestos, promoción interna, carrera profesional, situaciones administrativas, incompatibilidades y régimen sancionador. Ley 19/2013 de transparencia. Agenda 2030 y ODS.",
|
||||||
|
archivo: "bloque1/tema4.md"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
num: 5,
|
||||||
|
titulo: "Políticas de igualdad y contra la violencia de género. Igualdad LGTBI. Discapacidad y dependencia.",
|
||||||
|
archivo: "bloque1/tema5.md"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
num: 6,
|
||||||
|
titulo: "Sociedad de la información. Identidad y firma electrónica. DNIe. Agenda Digital para España.",
|
||||||
|
archivo: "bloque1/tema6.md"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
num: 7,
|
||||||
|
titulo: "Protección de datos personales: principios, derechos y obligaciones. Derechos digitales.",
|
||||||
|
archivo: "bloque1/tema7.md"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
num: 8,
|
||||||
|
titulo: "Acceso electrónico a los servicios públicos. Registros, notificaciones, medios electrónicos. ENS y ENI. NTI.",
|
||||||
|
archivo: "bloque1/tema8.md"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
num: 9,
|
||||||
|
titulo: "Instrumentos de acceso electrónico: sedes electrónicas, canales, identificación y autenticación. Infraestructuras y servicios comunes.",
|
||||||
|
archivo: "bloque1/tema9.md"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
titulo: "Tecnología básica",
|
||||||
|
color: "#c586c0",
|
||||||
|
temas: [
|
||||||
|
{
|
||||||
|
num: 1,
|
||||||
|
titulo: "Informática básica. Representación y comunicación de la información: elementos constitutivos de un sistema de información. Características y funciones. Arquitectura de ordenadores. Componentes internos de los equipos microinformáticos.",
|
||||||
|
archivo: "bloque2/tema1.md"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
num: 2,
|
||||||
|
titulo: "Periféricos: conectividad y administración. Elementos de impresión. Elementos de almacenamiento. Elementos de visualización y digitalización.",
|
||||||
|
archivo: "bloque2/tema2.md"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
num: 3,
|
||||||
|
titulo: "Tipos abstractos y Estructuras de datos. Organizaciones de ficheros. Algoritmos. Formatos de información y ficheros.",
|
||||||
|
archivo: "bloque2/tema3.md"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
num: 4,
|
||||||
|
titulo: "Sistemas operativos. Características y elementos constitutivos. Sistemas Windows. Sistemas Unix y Linux. Sistemas operativos para dispositivos móviles.",
|
||||||
|
archivo: "bloque2/tema4.md"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
num: 5,
|
||||||
|
titulo: "Sistemas de gestión de bases de datos relacionales, orientados a objetos y NoSQL: características y componentes.",
|
||||||
|
archivo: "bloque2/tema5.md"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 3,
|
||||||
|
titulo: "Desarrollo de sistemas",
|
||||||
|
color: "#d7ba7d",
|
||||||
|
temas: [
|
||||||
|
{
|
||||||
|
num: 1,
|
||||||
|
titulo: "Modelado de datos, metodologías y reglas. Entidades, atributos y relaciones. Diseño de bases de datos. Diseño lógico y físico. El modelo lógico relacional. Normalización.",
|
||||||
|
archivo: "bloque3/tema1.md"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
num: 2,
|
||||||
|
titulo: "Lenguajes de programación. Representación de tipos de datos. Operadores. Instrucciones condicionales. Bucles y recursividad. Procedimientos, funciones y parámetros. Vectores y registros. Estructura de un programa.",
|
||||||
|
archivo: "bloque3/tema2.md"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
num: 3,
|
||||||
|
titulo: "Lenguajes de interrogación de bases de datos. Estándar ANSI SQL. Procedimientos almacenados. Eventos y disparadores.",
|
||||||
|
archivo: "bloque3/tema3.md"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
num: 4,
|
||||||
|
titulo: "Diseño y programación orientada a objetos. Elementos y componentes software: objetos, clases, herencia, métodos, sobrecarga. Ventajas e inconvenientes. Patrones de diseño y lenguaje de modelado unificado (UML).",
|
||||||
|
archivo: "bloque3/tema4.md"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
num: 5,
|
||||||
|
titulo: "Arquitectura Java EE/Jakarta EE y plataforma .NET: componentes, persistencia y seguridad. Características, elementos, lenguajes y funciones en ambos entornos. Desarrollo de interfaces.",
|
||||||
|
archivo: "bloque3/tema5.md"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
num: 6,
|
||||||
|
titulo: "Arquitectura de sistemas cliente/servidor y multicapas: componentes y operación. Arquitecturas de servicios web y protocolos asociados.",
|
||||||
|
archivo: "bloque3/tema6.md"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
num: 7,
|
||||||
|
titulo: "Aplicaciones web. Desarrollo web front-end y en servidor, multiplataforma y multidispositivo. Lenguajes: HTML, XML y sus derivaciones. Navegadores y lenguajes de programación web. Lenguajes de script.",
|
||||||
|
archivo: "bloque3/tema7.md"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
num: 8,
|
||||||
|
titulo: "Accesibilidad, diseño universal y usabilidad. Acceso y usabilidad de las tecnologías, productos y servicios relacionados con la sociedad de la información. Confidencialidad y disponibilidad de la información en puestos de usuario final. Conceptos de seguridad en el desarrollo de los sistemas.",
|
||||||
|
archivo: "bloque3/tema8.md"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
num: 9,
|
||||||
|
titulo: "Repositorios: estructura y actualización. Generación de código y documentación. Metodologías de desarrollo. Pruebas. Programas para control de versiones. Plataformas de desarrollo colaborativo de software.",
|
||||||
|
archivo: "bloque3/tema9.md"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 4,
|
||||||
|
titulo: "Sistemas y comunicaciones",
|
||||||
|
color: "#4ec9b0",
|
||||||
|
temas: [
|
||||||
|
{
|
||||||
|
num: 1,
|
||||||
|
titulo: "Administración del Sistema operativo y software de base. Actualización, mantenimiento y reparación del sistema operativo.",
|
||||||
|
archivo: "bloque4/tema1.md"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
num: 2,
|
||||||
|
titulo: "Administración de bases de datos. Sistemas de almacenamiento y su virtualización. Políticas, sistemas y procedimientos de backup y su recuperación. Backup de sistemas físicos y virtuales. Virtualización de sistemas y virtualización de puestos de usuario.",
|
||||||
|
archivo: "bloque4/tema2.md"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
num: 3,
|
||||||
|
titulo: "Administración de servidores de correo electrónico y sus protocolos. Administración de contenedores y microservicios.",
|
||||||
|
archivo: "bloque4/tema3.md"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
num: 4,
|
||||||
|
titulo: "Administración de redes de área local. Gestión de usuarios. Gestión de dispositivos. Monitorización y control de tráfico.",
|
||||||
|
archivo: "bloque4/tema4.md"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
num: 5,
|
||||||
|
titulo: "Conceptos de seguridad de los sistemas de información. Seguridad física. Seguridad lógica. Amenazas y vulnerabilidades. Técnicas criptográficas y protocolos seguros. Mecanismos de firma digital. Infraestructura física de un CPD: acondicionamiento y equipamiento. Sistemas de gestión de incidencias. Control remoto de puestos de usuario.",
|
||||||
|
archivo: "bloque4/tema5.md"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
num: 6,
|
||||||
|
titulo: "Comunicaciones. Medios de transmisión. Modos de comunicación. Equipos terminales y equipos de interconexión y conmutación. Redes de comunicaciones. Redes de conmutación y redes de difusión. Comunicaciones móviles e inalámbricas.",
|
||||||
|
archivo: "bloque4/tema6.md"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
num: 7,
|
||||||
|
titulo: "El modelo TCP/IP y el modelo de referencia de interconexión de sistemas abiertos (OSI) de ISO. Protocolos TCP/IP.",
|
||||||
|
archivo: "bloque4/tema7.md"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
num: 8,
|
||||||
|
titulo: "Internet: arquitectura de red. Origen, evolución y estado actual. Principales servicios. Protocolos HTTP, HTTPS y SSL/TLS.",
|
||||||
|
archivo: "bloque4/tema8.md"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
num: 9,
|
||||||
|
titulo: "Seguridad y protección en redes de comunicaciones. Seguridad perimetral. Acceso remoto seguro a redes. Redes privadas virtuales (VPN). Seguridad en el puesto del usuario.",
|
||||||
|
archivo: "bloque4/tema9.md"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
num: 10,
|
||||||
|
titulo: "Redes locales. Tipología. Técnicas de transmisión. Métodos de acceso. Dispositivos de interconexión.",
|
||||||
|
archivo: "bloque4/tema10.md"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Total de temas en el temario.
|
||||||
|
*/
|
||||||
|
const TOTAL_TEMAS = TEMARIO.reduce((acc, b) => acc + b.temas.length, 0);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dado un bloque (1-based) y un num de tema (1-based) devuelve
|
||||||
|
* el tema anterior y el siguiente en orden lineal, o null.
|
||||||
|
*/
|
||||||
|
function getNavigation(bloqueId, temaNum) {
|
||||||
|
const flat = [];
|
||||||
|
for (const bloque of TEMARIO) {
|
||||||
|
for (const tema of bloque.temas) {
|
||||||
|
flat.push({ bloqueId: bloque.id, temaNum: tema.num });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const idx = flat.findIndex(x => x.bloqueId === bloqueId && x.temaNum === temaNum);
|
||||||
|
return {
|
||||||
|
prev: idx > 0 ? flat[idx - 1] : null,
|
||||||
|
next: idx < flat.length - 1 ? flat[idx + 1] : null,
|
||||||
|
pos: idx + 1,
|
||||||
|
total: flat.length
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,179 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="es">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>TAI – AGE | Noticias y Convocatorias</title>
|
||||||
|
<link rel="stylesheet" href="css/style.css">
|
||||||
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.0/css/all.min.css">
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;700&display=swap" rel="stylesheet">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<!-- ── Topbar ─────────────────────────────────────────── -->
|
||||||
|
<nav class="topbar">
|
||||||
|
<span class="topbar-brand"><i class="fas fa-graduation-cap"></i> TAI – AGE</span>
|
||||||
|
<nav class="topbar-nav">
|
||||||
|
<a href="index.html">Inicio</a>
|
||||||
|
<a href="curso.html">Temario</a>
|
||||||
|
<a href="cuestionarios/index.html">Cuestionarios</a>
|
||||||
|
<a href="noticias.html" class="active">Noticias</a>
|
||||||
|
</nav>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<div class="noticias-layout" style="margin-top:calc(var(--topbar-h) + 2rem)">
|
||||||
|
|
||||||
|
<div class="noticias-header">
|
||||||
|
<h1><i class="fas fa-bell"></i> Noticias y Convocatorias</h1>
|
||||||
|
<p>Actualiza esta página para ver los últimos avisos del INAP, novedades del BOE y nuevas convocatorias de las CCAA.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ── INAP – Convocatoria TAI ─────────────────────── -->
|
||||||
|
<section class="noticias-section">
|
||||||
|
<div class="noticias-section-hdr">
|
||||||
|
<h2><i class="fas fa-university"></i> INAP – Convocatoria TAI en curso</h2>
|
||||||
|
<span class="noticias-src-badge">inap.es</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="inap-links-grid">
|
||||||
|
<a href="https://sede.inap.gob.es/es/procedimientos-y-servicios/seleccion/procesos-selectivos-de-cuerpos-y-escalas-generales/cuerpo-de-tecnicos-auxiliares-de-informatica-de-la-administracion-del-estado-ingreso-libre-convocatoria-2025" target="_blank" rel="noopener" class="inap-link-card inap-link-primary">
|
||||||
|
<i class="fas fa-bullhorn"></i>
|
||||||
|
<span>Convocatoria TAI – INAP</span>
|
||||||
|
<i class="fas fa-external-link-alt inap-ext"></i>
|
||||||
|
</a>
|
||||||
|
<a href="https://sede.inap.gob.es/es/procedimientos-y-servicios/seleccion/procesos-selectivos-de-cuerpos-y-escalas-generales" target="_blank" rel="noopener" class="inap-link-card">
|
||||||
|
<i class="fas fa-list-ul"></i>
|
||||||
|
<span>Procesos selectivos en curso</span>
|
||||||
|
<i class="fas fa-external-link-alt inap-ext"></i>
|
||||||
|
</a>
|
||||||
|
<a href="https://www.boe.es/buscar/doc.php?campo[0]=TITN&dato[0]=tai" target="_blank" rel="noopener" class="inap-link-card">
|
||||||
|
<i class="fas fa-search"></i>
|
||||||
|
<span>Buscar "TAI" en el BOE</span>
|
||||||
|
<i class="fas fa-external-link-alt inap-ext"></i>
|
||||||
|
</a>
|
||||||
|
<a href="https://www.boe.es/buscar/boe.php?campo[0]=TITN&dato[0]=tecnico+administracion+informatica&campo[1]=DDES&dato[1]=&field=todos&texto=&desde=&hasta=&sort_field[0]=fech&sort_order[0]=desc&accion=buscar" target="_blank" rel="noopener" class="inap-link-card">
|
||||||
|
<i class="fas fa-file-alt"></i>
|
||||||
|
<span>Resoluciones TAI en BOE</span>
|
||||||
|
<i class="fas fa-external-link-alt inap-ext"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="inap-info-box">
|
||||||
|
<i class="fas fa-info-circle"></i>
|
||||||
|
<p>El INAP publica actualizaciones de la convocatoria (listas provisionales, definitivas, llamamientos, fechas de examen) en su web oficial y en el BOE Sección II-B.
|
||||||
|
Revisa periódicamente la página de la convocatoria TAI o configura una alerta en el BOE para no perderte ninguna resolución.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- ── BOE – Oposiciones y concursos ──────────────── -->
|
||||||
|
<section class="noticias-section">
|
||||||
|
<div class="noticias-section-hdr">
|
||||||
|
<h2><i class="fas fa-newspaper"></i> BOE – Oposiciones y Concursos (AGE)</h2>
|
||||||
|
<span id="boe-fecha" class="noticias-src-badge">Cargando…</span>
|
||||||
|
</div>
|
||||||
|
<p class="noticias-section-desc">Últimas publicaciones de la Sección II-B del BOE (Oposiciones, concursos y vacantes). Las entradas relacionadas con TAI / informática se marcan en verde.</p>
|
||||||
|
|
||||||
|
<div id="boe-items" class="news-list">
|
||||||
|
<div class="news-loading"><i class="fas fa-spinner fa-spin"></i> Cargando noticias del BOE…</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<a href="https://www.boe.es/diario_boe/ultimos_dias.php" target="_blank" rel="noopener" class="btn btn-outline" style="margin-top:1rem;font-size:.85rem">
|
||||||
|
<i class="fas fa-external-link-alt"></i> Ver BOE completo
|
||||||
|
</a>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- ── Boletines CCAA ──────────────────────────────── -->
|
||||||
|
<section class="noticias-section">
|
||||||
|
<div class="noticias-section-hdr">
|
||||||
|
<h2><i class="fas fa-map-marked-alt"></i> Boletines Oficiales de las CCAA</h2>
|
||||||
|
<span class="noticias-src-badge">17 CCAA + Ceuta + Melilla</span>
|
||||||
|
</div>
|
||||||
|
<p class="noticias-section-desc">Acceso directo a los boletines oficiales autonómicos para consultar convocatorias de cuerpos de informática de cada comunidad.</p>
|
||||||
|
|
||||||
|
<div class="ccaa-grid">
|
||||||
|
<a href="https://www.juntadeandalucia.es/boja" target="_blank" rel="noopener" class="ccaa-card">
|
||||||
|
<span class="ccaa-sigla">BOJA</span>
|
||||||
|
<span class="ccaa-nombre">Andalucía</span>
|
||||||
|
</a>
|
||||||
|
<a href="https://www.boa.aragon.es" target="_blank" rel="noopener" class="ccaa-card">
|
||||||
|
<span class="ccaa-sigla">BOA</span>
|
||||||
|
<span class="ccaa-nombre">Aragón</span>
|
||||||
|
</a>
|
||||||
|
<a href="https://sede.asturias.es/bopa" target="_blank" rel="noopener" class="ccaa-card">
|
||||||
|
<span class="ccaa-sigla">BOPA</span>
|
||||||
|
<span class="ccaa-nombre">Asturias</span>
|
||||||
|
</a>
|
||||||
|
<a href="https://www.caib.es/boib" target="_blank" rel="noopener" class="ccaa-card">
|
||||||
|
<span class="ccaa-sigla">BOIB</span>
|
||||||
|
<span class="ccaa-nombre">Baleares</span>
|
||||||
|
</a>
|
||||||
|
<a href="https://www.gobiernodecanarias.org/librodigital/boc/" target="_blank" rel="noopener" class="ccaa-card">
|
||||||
|
<span class="ccaa-sigla">BOC</span>
|
||||||
|
<span class="ccaa-nombre">Canarias</span>
|
||||||
|
</a>
|
||||||
|
<a href="https://boc.cantabria.es" target="_blank" rel="noopener" class="ccaa-card">
|
||||||
|
<span class="ccaa-sigla">BOC</span>
|
||||||
|
<span class="ccaa-nombre">Cantabria</span>
|
||||||
|
</a>
|
||||||
|
<a href="https://docm.castillalamancha.es" target="_blank" rel="noopener" class="ccaa-card">
|
||||||
|
<span class="ccaa-sigla">DOCM</span>
|
||||||
|
<span class="ccaa-nombre">Castilla-La Mancha</span>
|
||||||
|
</a>
|
||||||
|
<a href="https://bocyl.jcyl.es" target="_blank" rel="noopener" class="ccaa-card">
|
||||||
|
<span class="ccaa-sigla">BOCYL</span>
|
||||||
|
<span class="ccaa-nombre">Castilla y León</span>
|
||||||
|
</a>
|
||||||
|
<a href="https://dogc.gencat.cat" target="_blank" rel="noopener" class="ccaa-card">
|
||||||
|
<span class="ccaa-sigla">DOGC</span>
|
||||||
|
<span class="ccaa-nombre">Cataluña</span>
|
||||||
|
</a>
|
||||||
|
<a href="https://doe.juntaex.es" target="_blank" rel="noopener" class="ccaa-card">
|
||||||
|
<span class="ccaa-sigla">DOE</span>
|
||||||
|
<span class="ccaa-nombre">Extremadura</span>
|
||||||
|
</a>
|
||||||
|
<a href="https://www.xunta.gal/dog" target="_blank" rel="noopener" class="ccaa-card">
|
||||||
|
<span class="ccaa-sigla">DOG</span>
|
||||||
|
<span class="ccaa-nombre">Galicia</span>
|
||||||
|
</a>
|
||||||
|
<a href="https://www.larioja.org/es/gobierno-rioja/boletines" target="_blank" rel="noopener" class="ccaa-card">
|
||||||
|
<span class="ccaa-sigla">BOR</span>
|
||||||
|
<span class="ccaa-nombre">La Rioja</span>
|
||||||
|
</a>
|
||||||
|
<a href="https://www.bocm.es" target="_blank" rel="noopener" class="ccaa-card">
|
||||||
|
<span class="ccaa-sigla">BOCM</span>
|
||||||
|
<span class="ccaa-nombre">Madrid</span>
|
||||||
|
</a>
|
||||||
|
<a href="https://www.borm.es" target="_blank" rel="noopener" class="ccaa-card">
|
||||||
|
<span class="ccaa-sigla">BORM</span>
|
||||||
|
<span class="ccaa-nombre">Murcia</span>
|
||||||
|
</a>
|
||||||
|
<a href="https://bon.navarra.es" target="_blank" rel="noopener" class="ccaa-card">
|
||||||
|
<span class="ccaa-sigla">BON</span>
|
||||||
|
<span class="ccaa-nombre">Navarra</span>
|
||||||
|
</a>
|
||||||
|
<a href="https://www.euskadi.eus/bopv" target="_blank" rel="noopener" class="ccaa-card">
|
||||||
|
<span class="ccaa-sigla">BOPV</span>
|
||||||
|
<span class="ccaa-nombre">País Vasco</span>
|
||||||
|
</a>
|
||||||
|
<a href="https://dogv.gva.es" target="_blank" rel="noopener" class="ccaa-card">
|
||||||
|
<span class="ccaa-sigla">DOGV</span>
|
||||||
|
<span class="ccaa-nombre">C. Valenciana</span>
|
||||||
|
</a>
|
||||||
|
<a href="https://www.ceuta.es/ceuta/index.php/boletin-oficial" target="_blank" rel="noopener" class="ccaa-card">
|
||||||
|
<span class="ccaa-sigla">BOCCE</span>
|
||||||
|
<span class="ccaa-nombre">Ceuta</span>
|
||||||
|
</a>
|
||||||
|
<a href="https://www.melilla.es/melillaPortal/contenedor1.jsp?seccion=s_lofb_d4_v1.jsp&contenido=17547&nivel=1400&tipo=6&codResi=1&language=es" target="_blank" rel="noopener" class="ccaa-card">
|
||||||
|
<span class="ccaa-sigla">BOME</span>
|
||||||
|
<span class="ccaa-nombre">Melilla</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="js/noticias.js"></script>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
Loading…
Reference in New Issue