Files
Uriel JarethandClaude Opus 5 0b9c9c1dc2 Portal interno INSTA: base de conocimiento con Magic Login y permisos por rol
Base de conocimiento para el personal del Instituto Santiago Tapia: oferta
educativa (en tarjetas y en tabla), requisitos y titulacion, becas y fechas
de inscripcion, links de clases de Google Meet, preguntas frecuentes,
mensajes listos y datos del instituto.

Acceso con Magic Login: enlace de un solo uso, 15 minutos de vigencia, y en
la base solo se guarda el SHA-256 del token. El canje es atomico, asi que dos
canjes simultaneos del mismo enlace no pueden crear dos sesiones.

Tres roles con 19 permisos. El catalogo lo edita direccion; los links de
clases, direccion y administracion; el resto del equipo consulta. El menu se
pinta con lo que el servidor autoriza y cada endpoint exige la misma clave,
asi que no hay pantallas alcanzables escribiendo la direccion a mano.

Toda edicion pasa por una lista blanca de campos validados y queda en la
bitacora. Un precio vacio no se guarda como cero, un link que no es una
direccion se rechaza, y no se puede marcar una validez oficial como
confirmada sin numero de acuerdo.

Pensado para leerse comodo: texto de 17 px, contraste AA, areas de toque de
44 px, tema claro y bloques plegados en telefono.

La oferta, los costos, las becas, los requisitos y el contacto son los reales
del instituto, tomados del CSV de oferta educativa y de la base de
conocimiento. Los usuarios de acceso y los links de Meet son de demostracion.

Verificado: 103 comprobaciones de API y 51 pruebas en WebKit escritorio y
iPhone 13.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
2026-07-29 23:20:34 -06:00

56 lines
2.0 KiB
HTML

<!doctype html>
<html lang="es-MX">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Portal interno · Instituto Santiago Tapia</title>
<meta name="color-scheme" content="light">
<link rel="icon" href="/assets/insta-logo.webp">
<link rel="stylesheet" href="/css/insta.css">
</head>
<body>
<div class="portal">
<!-- Barra lateral: en móvil se vuelve cajón deslizable -->
<aside class="barra" id="barra" aria-label="Secciones del portal">
<div class="barra__cabeza">
<a class="rotulo" href="/portal">
<img src="/assets/insta-logo.webp" width="44" height="28" alt="">
<span class="rotulo__texto">
<span class="rotulo__nombre">INSTA</span>
<span class="rotulo__sub">Portal interno</span>
</span>
</a>
</div>
<nav class="barra__nav" id="nav"></nav>
<div class="barra__pie" id="pie-barra"></div>
</aside>
<div>
<!-- Tope móvil -->
<header class="tope">
<button class="tope__menu" id="abrir-menu" type="button" aria-label="Abrir menú" aria-expanded="false" aria-controls="barra"></button>
<a class="rotulo" href="/portal">
<img src="/assets/insta-logo.webp" width="38" height="24" alt="">
<span class="rotulo__texto"><span class="rotulo__nombre">INSTA</span></span>
</a>
<span id="ficha-movil"></span>
</header>
<main class="lienzo">
<div class="lienzo__interno" id="vista" aria-live="polite">
<div class="cargando">
<div class="hueso" style="height:120px"></div>
<div class="hueso"></div>
<div class="hueso"></div>
</div>
</div>
</main>
</div>
</div>
<div class="brindis" id="brindis" role="status" aria-live="polite"><span class="brindis__marca"></span><span id="brindis-txt"></span></div>
<script type="module" src="/js/app.mjs"></script>
</body>
</html>