feat(guias): agregar resolución paso a paso

Co-authored-by: wsf <alejandro.sartorio@gmail.com>
Signed-off-by: wsf <alejandro.sartorio@gmail.com>
This commit is contained in:
wsf
2026-08-25 06:53:49 -03:00
parent 0c55a7ce82
commit f96428cf3e
7 changed files with 117 additions and 2 deletions
+3
View File
@@ -61,6 +61,9 @@ sap_tfi_2026/
> **Practicar con ejercicios interactivos:**
> [https://cursos-uai.github.io/sap_tfi_2026/ejercicios.html](https://cursos-uai.github.io/sap_tfi_2026/ejercicios.html)
> **Resolver casos con hojas de ruta paso a paso:**
> [https://cursos-uai.github.io/sap_tfi_2026/guias.html](https://cursos-uai.github.io/sap_tfi_2026/guias.html)
El directorio **`prototipo/`** contiene una representación interactiva de las
pantallas que actúan como fronteras (*boundaries*) en los diagramas de robustez.
Permite observar cómo los botones, formularios, tablas, mensajes y otros
+7
View File
@@ -47,6 +47,13 @@ Los ejercicios ofrecen corrección inmediata, explicación de la respuesta y
progreso persistente en el navegador. La base teórica se encuentra citada al
pie de cada estación.
## Guías paso a paso
Abrir `guias.html` para resolver seis casos progresivos con una hoja de ruta:
comprender el problema, anticipar cambios, descomponer, diseñar interfaces,
establecer relaciones y validar el diseño. Cada paso permite registrar el
razonamiento, solicitar una pista y comparar al final con una solución modelo.
## Alcance
Es un prototipo educativo, no una reproducción exacta ni conectada a Odoo. Los
+1 -1
View File
@@ -11,7 +11,7 @@
<body class="lab-body">
<header class="topbar">
<a class="brand" href="index.html"><span class="brand-mark">S</span><span><strong>SAP · TFI</strong><small>Laboratorio ICONIX</small></span></a>
<nav class="course lab-links"><a href="index.html">Prototipo</a><a class="active" href="ejercicios.html">Ejercicios</a></nav>
<nav class="course lab-links"><a href="index.html">Prototipo</a><a class="active" href="ejercicios.html">Ejercicios</a><a href="guias.html">Guías</a></nav>
</header>
<main class="lab-layout">
+2
View File
@@ -0,0 +1,2 @@
.guide-body{min-height:100vh}.guide-shell{max-width:1250px;margin:auto;padding:38px clamp(16px,4vw,52px) 60px}.guide-hero{display:flex;justify-content:space-between;align-items:end;gap:25px}.guide-hero h1{font:700 clamp(34px,5vw,58px)/1 Georgia,serif;margin:0}.guide-hero>div:first-child>p:last-child{max-width:760px;color:var(--muted);font-size:17px}.guide-progress{background:#172433;color:white;border-radius:14px;padding:18px 25px;min-width:150px}.guide-progress strong{font:700 27px Georgia,serif;display:block}.guide-progress span{color:#b7c1cb;font-size:12px}.case-picker{display:grid;grid-template-columns:repeat(6,1fr);gap:8px;margin:28px 0}.case-button{border:1px solid var(--line);background:white;border-radius:10px;padding:12px;text-align:left;cursor:pointer;color:var(--ink)}.case-button span{display:block;color:var(--muted);font-size:10px;margin-bottom:3px}.case-button.active{background:#172433;border-color:#172433;color:white}.case-button.done:not(.active){border-color:#5ab492;background:#edfaf5}.case-workspace{display:grid;grid-template-columns:245px 1fr;background:white;border:1px solid var(--line);border-radius:16px;overflow:hidden;box-shadow:var(--shadow)}.roadmap{background:#f5f7f8;padding:25px;border-right:1px solid var(--line)}.roadmap ol{padding:0;margin:0;list-style:none;display:grid;gap:6px}.roadmap li{display:grid;grid-template-columns:25px 1fr;padding:9px;border-radius:8px;color:var(--muted);font-size:12px}.roadmap li i{font-style:normal}.roadmap li.active{background:white;color:var(--blue);font-weight:800}.roadmap li.done{color:#087658}.step-panel{padding:28px min(5vw,55px);min-height:560px}.step-panel>header{display:flex;justify-content:space-between;align-items:start}.step-panel h2{font:700 32px Georgia,serif;margin:0}.step-panel>header>span{background:#edf1f4;padding:7px 10px;border-radius:18px;color:var(--muted);font-size:11px;font-weight:800}.problem-box{background:#172433;color:#d4dde5;border-radius:11px;padding:18px 20px;margin:20px 0}.problem-box b{color:white}.step-question h3{font-size:21px;margin-bottom:5px}.step-question>p{color:var(--muted);margin-top:0}.answer-area{width:100%;min-height:135px;border:1px solid var(--line);border-radius:9px;padding:14px;font:14px/1.5 ui-monospace,monospace;resize:vertical}.checklist{display:grid;gap:8px;margin:16px 0}.checklist label{display:flex;gap:9px;padding:10px;border:1px solid var(--line);border-radius:8px}.hint-box{display:none;background:#fff5df;border-left:4px solid #e19b27;padding:13px 15px;margin-top:13px;color:#704b13}.hint-box.visible{display:block}.step-actions{display:flex;gap:9px;margin-top:18px}.step-actions button,.solution-card button,dialog button{border:1px solid var(--line);background:white;padding:10px 14px;border-radius:8px;font-weight:800;cursor:pointer}.step-actions .primary,dialog button{margin-left:auto;background:var(--blue);border-color:var(--blue);color:white}.solution-card{border:1px solid #bce3d3;background:#effaf6;border-radius:10px;padding:18px}.solution-card h3{margin-top:0}.solution-card ul{padding-left:20px}.method-card{margin:28px 0;background:white;border:1px solid var(--line);border-radius:14px;padding:25px}.method-card h2{font:700 27px Georgia,serif}.method-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:10px}.method-grid span{background:#f5f7f8;padding:13px;border-radius:8px}.method-grid b{display:inline-grid;place-items:center;width:22px;height:22px;background:var(--blue);color:white;border-radius:50%;margin-right:8px;font-size:11px}.guide-sources{display:flex;gap:15px;flex-wrap:wrap;color:var(--muted);font-size:13px}.guide-sources a{color:var(--blue)}dialog{border:0;border-radius:15px;padding:30px;max-width:650px;box-shadow:0 25px 80px rgba(0,0,0,.3)}dialog::backdrop{background:rgba(15,25,35,.68)}.solution-mark{display:grid;place-items:center;width:38px;height:38px;border-radius:50%;background:#dff5eb;color:#087658;font-weight:900}dialog h2{font:700 28px Georgia,serif}.model-modules{display:grid;grid-template-columns:repeat(2,1fr);gap:9px}.model-module{border:1px solid var(--line);border-radius:8px;padding:12px}.model-module strong{display:block;color:var(--blue)}.model-module small{color:var(--muted)}
@media(max-width:900px){.guide-hero{align-items:start}.case-picker{grid-template-columns:repeat(3,1fr)}.case-workspace{grid-template-columns:1fr}.roadmap{border-right:0;border-bottom:1px solid var(--line)}.roadmap ol{display:flex;overflow:auto}.roadmap li{min-width:140px}.method-grid{grid-template-columns:1fr 1fr}}@media(max-width:600px){.guide-shell{padding-top:24px}.guide-hero{display:block}.guide-progress{display:inline-block}.case-picker{grid-template-columns:1fr 1fr}.step-panel{padding:22px 15px}.step-actions{flex-wrap:wrap}.step-actions .primary{margin-left:0}.method-grid,.model-modules{grid-template-columns:1fr}}
+47
View File
@@ -0,0 +1,47 @@
<!doctype html>
<html lang="es">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="Hojas de ruta para resolver problemas de diseño de software paso a paso">
<title>Guías paso a paso · SAP-TFI-2026</title>
<link rel="stylesheet" href="styles.css">
<link rel="stylesheet" href="guias.css">
</head>
<body class="guide-body">
<header class="topbar">
<a class="brand" href="index.html"><span class="brand-mark">S</span><span><strong>SAP · TFI</strong><small>Laboratorio ICONIX</small></span></a>
<nav class="course lab-links"><a href="index.html">Prototipo</a><a href="ejercicios.html">Ejercicios</a><a class="active" href="guias.html">Guías</a></nav>
</header>
<main class="guide-shell">
<section class="guide-hero">
<div><p class="eyebrow">Práctica acompañada</p><h1>Resolver diseño, paso a paso</h1><p>No mires primero la respuesta. Registrá tus decisiones, pedí una pista cuando la necesites y compará tu propuesta recién al final.</p></div>
<div class="guide-progress"><strong id="guide-completed">0 / 6</strong><span>casos completados</span></div>
</section>
<section class="case-picker" id="case-picker" aria-label="Casos disponibles"></section>
<section class="case-workspace">
<aside class="roadmap"><p class="eyebrow">Hoja de ruta</p><ol id="roadmap"></ol></aside>
<article class="step-panel">
<header><div><p class="eyebrow" id="case-label"></p><h2 id="case-title"></h2></div><span id="step-counter"></span></header>
<div class="problem-box" id="problem-box"></div>
<div id="step-content"></div>
<div class="step-actions"><button id="previous-step">← Paso anterior</button><button id="hint-button">Ver una pista</button><button id="next-step" class="primary">Guardar y continuar →</button></div>
</article>
</section>
<section class="method-card">
<p class="eyebrow">Método reutilizable para la práctica completa</p>
<h2>La misma hoja de ruta sirve para los problemas no resueltos</h2>
<div class="method-grid"><span><b>1</b>Separar hechos de decisiones</span><span><b>2</b>Anticipar cambios</span><span><b>3</b>Un secreto por módulo</span><span><b>4</b>Diseñar interfaces</span><span><b>5</b>Elegir relaciones</span><span><b>6</b>Documentar y someter a cambios</span></div>
<p>Aplicala a Celda de producción, Museo, Balanza de camiones, Procesamiento de imágenes, Estación climatológica, Exclusa y los demás enunciados de la práctica.</p>
</section>
<footer class="guide-sources"><b>Material de estudio:</b> <a href="https://www.fceia.unr.edu.ar/ingsoft/p-diseno.pdf" target="_blank" rel="noreferrer">Práctica de Diseño ↗</a><a href="https://www.fceia.unr.edu.ar/ingsoft/problemasDiseno.pdf" target="_blank" rel="noreferrer">Problemas resueltos ↗</a></footer>
</main>
<dialog id="solution-dialog"><div class="solution-mark">✓</div><h2>Compará con la solución modelo</h2><div id="solution-content"></div><button id="close-solution">Cerrar</button></dialog>
<script src="guias.js"></script>
</body>
</html>
+56
View File
@@ -0,0 +1,56 @@
const cases=[
{id:"limite",short:"Límite",title:"Controlar extracciones",problem:"Una caja de ahorros ya permite depositar, extraer y consultar saldo. Se agrega la regla: no extraer más de $10.000.",steps:[
["Separá lo estable de lo nuevo","¿Qué responsabilidad ya existe y cuál aparece con el nuevo requisito?","No confundas operaciones básicas de la cuenta con la política de límite."],
["Detectá los ítems de cambio","¿Qué podría variar de forma independiente? Anotá al menos dos cambios futuros.","El monto puede cambiar, desaparecer o incorporar excepciones."],
["Elegí los módulos","¿Qué secreto debería ocultar cada módulo?","Un módulo conserva la cuenta básica y otro la regla de extracción."],
["Diseñá las interfaces","Escribí las operaciones públicas. ¿Conviene que ambos módulos presenten la misma interfaz?","Una interfaz compatible permite sustituir el objeto sin modificar clientes."],
["Definí la relación","¿Quién usa o delega en quién? Representalo con una flecha.","El control envuelve o compone la cuenta y delega las operaciones permitidas."],
["Someté el diseño a cambios","Probá cambiar $10.000 por un límite diario y luego eliminar la regla. ¿Qué módulos tocás?","El diseño es fuerte si CajaAhorros permanece intacta."]],solution:{idea:"Separar la política de la cuenta básica y usar delegación.",modules:[["CajaAhorros","Oculta representación y algoritmos del saldo."],["ControlExtraccion","Oculta la regla del límite y delega en CajaAhorros."]],relation:"ControlExtraccion ◇──▷ CajaAhorros; interfaces compatibles."}},
{id:"form",short:"Formulario",title:"Validar nombre y DNI",problem:"Un formulario recibe nombre y DNI. Al aceptar, el sistema verifica que el DNI exista y corresponda con el nombre usando una fuente de datos.",steps:[
["Marcá las decisiones","¿Qué decisiones distintas hay en interfaz, regla y persistencia?","Pensá qué cambia si reemplazás GUI, regla o base relacional por separado."],
["Listá cambios probables","Proponé una variación para cada decisión.","La presentación puede ser web; la regla puede pedir otro dato; la fuente puede ser una API."],
["Asigná un secreto por módulo","Nombrá tres módulos y escribí el secreto de cada uno.","Frontera, regla de negocio y acceso a datos son tres motivos de cambio."],
["Definí servicios abstractos","Diseñá interfaces sin SQL, widgets ni rutas de archivos.","La regla pregunta por una persona; no debería conocer SELECT ni tablas."],
["Trazá la estructura de uso","Ordená: actor, formulario, control, repositorio.","La dependencia avanza desde la frontera hacia servicios abstractos."],
["Revisá con ICONIX","Mapeá los módulos a Boundary, Control y Entity/Repository.","Formulario=Boundary; validación=Control; acceso a personas=servicio de datos."]],solution:{idea:"Aislar presentación, regla de validación y acceso a datos.",modules:[["FormNombreDNI","Oculta la implementación del formulario."],["ControlarNombreDNI","Oculta la regla de correspondencia."],["RepositorioPersonas","Oculta tecnología y esquema de persistencia."]],relation:"Form → Control → Repositorio; ninguna sentencia SQL en los dos primeros."}},
{id:"cuentas",short:"Cuentas",title:"Modelar tipos de cuenta",problem:"Caja de ahorro y cuenta corriente comparten operaciones, pero la primera no permite saldo negativo y la segunda sí.",steps:[
["Compará comportamiento","Armá una tabla: qué operaciones comparten y cuáles cambian.","depositar y saldo son equivalentes; extraer tiene reglas distintas."],
["Evitá banderas de tipo","Explicá el costo de una clase con tipo=CA/CC y condicionales.","Cada nuevo tipo agrega ramas y mezcla secretos."],
["Separá implementaciones","Proponé una clase para cada variación de comportamiento.","Cada cuenta puede implementar extraer sin preguntar qué tipo es."],
["Extraé una interfaz común","¿Qué abstracción permite tratar todas las cuentas de manera uniforme?","Una CuentaBancaria abstracta declara el contrato compartido."],
["Elegí herencia con criterio","Dibujá generalización y justificá por qué es herencia de interfaz.","La sustitución sirve para clientes como calcularSaldoTotal."],
["Probá extensión","Agregá una cuenta remunerada. ¿Cuántas clases existentes modificás?","El resultado deseable es agregar un heredero sin modificar los anteriores."]],solution:{idea:"Polimorfismo en lugar de una bandera de tipo y condicionales.",modules:[["CuentaBancaria","Interfaz abstracta común."],["CajaAhorros","Regla de extracción con saldo suficiente."],["CuentaCorriente","Permite saldo negativo."]],relation:"CajaAhorros y CuentaCorriente heredan la interfaz CuentaBancaria."}},
{id:"idioma",short:"Idioma",title:"Internacionalizar un formulario",problem:"El título y las etiquetas del formulario Nombre/DNI deben cambiar según el idioma configurado.",steps:[
["Localizá los condicionales","¿Dónde aparecerían sentencias case si resolvieras todo dentro de dibujar()?","Habría ramas para título, etiquetas y botones."],
["Separá motivos de cambio","¿Qué cambia por idioma y qué cambia por tecnología de interfaz?","Traducción y dibujo son decisiones independientes."],
["Proponé una abstracción","¿Qué servicio puede pedir el formulario sin conocer un idioma concreto?","Puede solicitar textos mediante claves o una interfaz de recursos."],
["Elegí implementaciones","Definí al menos dos implementaciones intercambiables.","RecursosES y RecursosEN implementan el mismo contrato."],
["Usá composición","Mostrá cómo recibe el formulario su proveedor de textos.","El proveedor se inyecta al construir el formulario."],
["Aplicá abierto/cerrado","Agregá portugués y contá qué cambia.","Se agrega una implementación y configuración; el formulario no cambia."]],solution:{idea:"Separar recursos lingüísticos de la implementación visual.",modules:[["RecursosFormulario","Interfaz de textos necesarios."],["RecursosES / RecursosEN","Implementaciones por idioma."],["FormNombreDNI","Dibuja usando un proveedor compuesto."]],relation:"FormNombreDNI ◇── RecursosFormulario; implementaciones sustituibles."}},
{id:"reuso",short:"Reuso",title:"Compartir implementación sin mezclar reglas",problem:"CajaAhorros y CuentaCorriente repiten el código de depositar y consultar saldo, pero difieren al extraer.",steps:[
["Identificá repetición","¿Qué código es realmente común y qué parte expresa una regla variable?","La representación del saldo es común; la autorización de extracción difiere."],
["Evaluá si es un ítem de cambio","¿La implementación común podría divergir entre tipos de cuenta?","Si puede divergir, heredar código crea acoplamiento evolutivo."],
["Compará alternativas","Contrastá copiar, heredar implementación y componer un colaborador.","La composición permite delegar sin forzar una jerarquía de implementación."],
["Diseñá el colaborador","Definí operaciones privadas de implementación del saldo.","Un objeto CuentaImp puede mantener y modificar el saldo."],
["Dibujá ambas relaciones","Mostrá generalización de interfaz y composición de implementación.","Son relaciones con propósitos distintos."],
["Documentá lo que UML no muestra","Escribí cómo delega cada operación y qué secreto queda oculto.","El diagrama solo no especifica interfaces, secretos ni delegación."]],solution:{idea:"Combinar herencia de interfaz con composición para compartir implementación.",modules:[["CuentaBancaria","Contrato común."],["CajaAhorros / CuentaCorriente","Reglas específicas."],["CuentaImp","Estado y operaciones básicas del saldo."]],relation:"Las cuentas heredan CuentaBancaria y cada instancia compone su propio CuentaImp."}},
{id:"secretos",short:"Secretos",title:"Un secreto por módulo",problem:"Un formulario localizado mezcla la forma de dibujarse con el idioma de sus textos. Debe poder cambiar cualquiera sin tocar la otra decisión.",steps:[
["Enumerá secretos","¿Cuántas decisiones independientes oculta hoy el formulario?","Al menos dos: presentación y traducción."],
["Refiná la granularidad","¿Conviene un módulo por título o un proveedor para todos los textos? Justificá.","La granularidad debe conservar cohesión sin crear interfaces inmanejables."],
["Abstraé la interfaz","Escribí servicios que no revelen archivos, diccionarios ni variables de entorno.","titulo(), etiquetaNombre() o texto(clave) describen necesidades, no almacenamiento."],
["Encapsulá implementaciones","Anotá qué detalles deben permanecer privados.","Codificación, archivo, locale y cache son internos al proveedor."],
["Configurá en un punto estable","Decidí dónde seleccionar el idioma concreto.","La composición se realiza en el punto de arranque, no dentro del formulario."],
["Cerrá con guía de módulos","Para cada módulo escribí Función, Secreto, Interfaz y Relaciones.","La figura UML comunica relaciones, pero no reemplaza esta documentación."]],solution:{idea:"La calidad surge de explicitar función, secreto, interfaz y delegación.",modules:[["FormNombreDNI","Oculta dibujo e interacción."],["TextosFormulario","Oculta selección y representación de textos."],["Configurador","Elige implementaciones al iniciar."]],relation:"Configurador crea Textos; Form lo recibe por composición y sólo conoce su interfaz."}}
];
const stepNames=["Comprender","Anticipar cambios","Descomponer","Diseñar interfaz","Relacionar","Validar"];
let caseIndex=0,stepIndex=0;
let state=JSON.parse(localStorage.getItem("iconix-guides")||"{}");
function key(){return`${cases[caseIndex].id}-${stepIndex}`}
function save(){localStorage.setItem("iconix-guides",JSON.stringify(state))}
function caseDone(c){return c.steps.every((_,i)=>state[`${c.id}-${i}`]?.done)}
function renderPicker(){document.querySelector("#case-picker").innerHTML=cases.map((c,i)=>`<button class="case-button ${i===caseIndex?"active":""} ${caseDone(c)?"done":""}" data-case="${i}"><span>CASO ${i+1}${caseDone(c)?" · ✓":""}</span>${c.short}</button>`).join("");document.querySelector("#guide-completed").textContent=`${cases.filter(caseDone).length} / ${cases.length}`}
function render(){const c=cases[caseIndex],s=c.steps[stepIndex],entry=state[key()]||{};document.querySelector("#case-label").textContent=`Caso ${caseIndex+1} · ${stepNames[stepIndex]}`;document.querySelector("#case-title").textContent=c.title;document.querySelector("#step-counter").textContent=`PASO ${stepIndex+1} DE 6`;document.querySelector("#problem-box").innerHTML=`<b>Enunciado de trabajo</b><br>${c.problem}`;document.querySelector("#roadmap").innerHTML=c.steps.map((x,i)=>`<li class="${i===stepIndex?"active":""} ${state[`${c.id}-${i}`]?.done?"done":""}" data-step="${i}"><i>${state[`${c.id}-${i}`]?.done?"✓":i+1}</i>${x[0]}</li>`).join("");document.querySelector("#step-content").innerHTML=stepIndex<5?`<div class="step-question"><h3>${s[0]}</h3><p>${s[1]}</p><textarea class="answer-area" id="answer" placeholder="Escribí tu razonamiento antes de pedir una pista…">${entry.answer||""}</textarea><div class="hint-box ${entry.hint?"visible":""}" id="hint-box"><b>Pista:</b> ${s[2]}</div><div class="checklist"><label><input type="checkbox" data-check="reason" ${entry.reason?"checked":""}> Justifiqué la decisión en función de cambios probables.</label><label><input type="checkbox" data-check="terms" ${entry.terms?"checked":""}> Usé vocabulario de diseño: secreto, interfaz, responsabilidad o relación.</label></div></div>`:`<div class="step-question"><h3>${s[0]}</h3><p>${s[1]}</p><textarea class="answer-area" id="answer" placeholder="Registrá el resultado de someter tu diseño a cambios…">${entry.answer||""}</textarea><div class="hint-box ${entry.hint?"visible":""}" id="hint-box"><b>Pista:</b> ${s[2]}</div><div class="solution-card"><h3>Antes de comparar</h3><p>Comprobá que cada módulo tenga un secreto, que las interfaces no revelen implementación y que un cambio afecte pocas unidades.</p><button id="reveal-solution">Comparar con la solución modelo</button></div></div>`;document.querySelector("#previous-step").disabled=stepIndex===0;document.querySelector("#next-step").textContent=stepIndex===5?"Completar caso ✓":"Guardar y continuar →";renderPicker()}
function persist(){const entry=state[key()]||{};entry.answer=document.querySelector("#answer")?.value||"";document.querySelectorAll("[data-check]").forEach(x=>entry[x.dataset.check]=x.checked);state[key()]=entry;save()}
function solutionHTML(c){return`<p>${c.solution.idea}</p><div class="model-modules">${c.solution.modules.map(m=>`<div class="model-module"><strong>${m[0]}</strong><small>${m[1]}</small></div>`).join("")}</div><p><b>Relación:</b> ${c.solution.relation}</p><p><small>Esta es una solución de referencia. Compará los criterios y no sólo los nombres.</small></p>`}
document.querySelector("#case-picker").addEventListener("click",e=>{const b=e.target.closest("[data-case]");if(b){persist();caseIndex=Number(b.dataset.case);stepIndex=0;render()}});document.querySelector("#roadmap").addEventListener("click",e=>{const li=e.target.closest("[data-step]");if(li){persist();stepIndex=Number(li.dataset.step);render()}});document.querySelector("#previous-step").addEventListener("click",()=>{persist();if(stepIndex>0)stepIndex--;render()});document.querySelector("#next-step").addEventListener("click",()=>{persist();state[key()].done=true;save();if(stepIndex<5)stepIndex++;else{document.querySelector("#solution-content").innerHTML=solutionHTML(cases[caseIndex]);document.querySelector("#solution-dialog").showModal()}render()});document.querySelector("#hint-button").addEventListener("click",()=>{const entry=state[key()]||{};entry.hint=true;state[key()]=entry;save();document.querySelector("#hint-box")?.classList.add("visible")});document.querySelector("#step-content").addEventListener("click",e=>{if(e.target.id==="reveal-solution"){persist();document.querySelector("#solution-content").innerHTML=solutionHTML(cases[caseIndex]);document.querySelector("#solution-dialog").showModal()}});document.querySelector("#close-solution").addEventListener("click",()=>document.querySelector("#solution-dialog").close());render();
+1 -1
View File
@@ -13,7 +13,7 @@
<span class="brand-mark">S</span>
<span><strong>SAP · TFI</strong><small>Laboratorio ICONIX</small></span>
</a>
<div class="course lab-links"><a class="active" href="index.html">Prototipo</a><a href="ejercicios.html">Ejercicios</a></div>
<div class="course lab-links"><a class="active" href="index.html">Prototipo</a><a href="ejercicios.html">Ejercicios</a><a href="guias.html">Guías</a></div>
</header>
<main class="workspace">