Onboarding
Cómo un usuario nuevo llega a entender y usar SkillNet. Este documento fija el modelo (principios + flujos), la arquitectura que lo sostiene, y un plan por fases. Es la vara de medir: cualquier pantalla futura del onboarding se contrasta contra esto.
Relacionado: degraded-mode-ux.md (estados sin clave), personalization.md (perfil del aprendiz), audience-modes.md (organización / individual).
1. Principio rector
Que libere, no que ate. SkillNet vende libertad y adaptación; el onboarding tiene que sentirse igual. El filtro de toda decisión: ¿esto para al usuario o lo libera?
De ahí, seis pilares:
- La plataforma nunca está vacía. Se arranca con un curso de demo ya montado (seed). Nada de tablero en blanco.
- Guía sí, pero puerta siempre abierta. Un “paso 1 de N” por defecto (mucha gente lo quiere y es el camino que más convierte) cerrable, reabrible y que recuerda el progreso. Cerrar no castiga con vacío: caes en la plataforma llena.
- El valor se descubre tocando, al instante. La primera victoria es contenido pre-generado (rápido, sin clave, sin esperar). Nunca una generación en vivo como bienvenida (lenta = derrota).
- El diferenciador es la UI generativa. No son plantillas con el texto cambiado: la interfaz de cada lección se compone para ese contenido y esa persona. Se hace evidente con un empujón ignorable (“mira la misma lección para otra persona → es distinta”).
- La personalización se ofrece o se infiere, nunca se impone. La captura de “cómo aprendes” es opcional; lo que no se pregunta se aprende del comportamiento.
- La clave (API key) desbloquea, no cobra. Sin clave exploras la demo; con clave lo haces con lo tuyo. Se llega a la clave cuando ya se vio el valor y se quiere.
2. La idea arquitectónica: onboarding dirigido por capacidades
El punto elegante: onboarding, degradación sin-clave y defaults inteligentes son el mismo
problema — “según lo que hay disponible y quién eres, muestra una cosa u otra”. Se resuelven
con una sola fuente de verdad en vez de if (hayClave) esparcidos por todo el código.
┌──────────────────────┐
settings/env │ Capabilities │ ai, generation, tutor, tts, images
claves │ (¿qué IA hay?) │ → GET /capabilities (o /setup/status)
└──────────┬───────────┘
│ useCapabilities()
┌───────────────────┼────────────────────┐
▼ ▼ ▼
Onboarding steps Elementos de UI Banners degradado
(filtrados) (<Gated requires>) (degraded-mode-ux)
2.1 Capabilities — la fuente de verdad
Un objeto derivado de la presencia (y validez) de claves, expuesto por el backend:
interface Capabilities {
ai: boolean // hay un LLM utilizable (nada funciona sin esto)
generation: boolean // generar cursos/lecciones
tutor: boolean // chat tutor
tts: boolean // voz (mascota / podcast) — degrada a offline, ver degraded-mode
images: boolean // infografías
}
- Backend: se calcula en un sitio (presencia de
LLM_API_KEY,TTS_API_KEY,OPENROUTER_API_KEY). HoyGET /setup/statusya existe y es el sitio natural (ya le añadimosonboarding_enabled). - Frontend: un hook
useCapabilities(). Cualquier pieza de IA lo consulta; nadie hardcodea “hay clave”.
2.2 Gating declarativo (elemento estático vs. necesita-clave)
En lugar de esparcir condicionales, un componente/hook único:
<Gated requires="tutor">
<TutorPromptChip /> {/* solo se pinta si capabilities.tutor */}
</Gated>
Regla: el elemento de IA se enciende solo si su capacidad está. Sin clave no se muestra (no un callejón/error). Ejemplos:
| Siempre (estático / pre-cocinado) | requires (se enciende con clave) |
|---|---|
| Curso demo (seed), verlo | Opción “otro” libre en preguntas (generation/ai) |
| Empujón de contraste (dos personas, seed) | Preview generado / “crea tu primer curso” (generation) |
| Presets fijos de las preguntas | Chat con el tutor (tutor) |
| Tour (joyride) | Personalización que regenera al vuelo (generation) |
Clave de diseño: el lado sin-clave debe sentirse completo (lo pre-cocinado carga el valor). Lo de IA es aditivo, no “lo bueno estaba escondido”.
2.3 Onboarding como datos, no como flujo hardcodeado
El tour es una lista declarativa de pasos; joyride solo la consume. Añadir/quitar/reordenar = editar datos.
interface OnboardingStep {
id: string
role: 'employee' | 'admin'
target: string // selector del elemento a resaltar
title: string; body: string
requires?: keyof Capabilities // se omite el paso si la capacidad no está
order: number
}
Filtro en runtime: steps.filter(role).filter(cap => !s.requires || capabilities[s.requires]).
Un paso que habla del tutor desaparece sin clave, sin ramas ad-hoc.
2.4 Estado del onboarding — por usuario, persistido, reabrible
interface OnboardingState { completed: boolean; dismissedAt?: string; lastStepId?: string }
- Ortogonal al routing: nunca redirige a la fuerza (ya existe el flag
ONBOARDING_ENABLED). - MVP:
localStorage. Fase posterior: campo por usuario (cross-device). - Reabrible desde un “?” persistente;
lastStepIdda el “recuerda dónde iba”.
2.5 Defaults inteligentes — un resolver desde el arquetipo de org
El admin da una pista mínima (educación / empresa) → un resolver mapea arquetipo → defaults.
type Archetype = 'education' | 'enterprise' | ...
function resolveDefaults(a: Archetype): OrgSettings // p.ej. enterprise ⇒ mascota off
- Un solo resolver, no condicionales dispersos.
- Los valores son defaults, siempre overridables (usuario/org).
- Toda decisión automática se muestra y se puede revertir (“Hemos desactivado la mascota porque es un entorno de empresa — cámbialo aquí”). Nunca magia silenciosa.
2.6 Demo compartido = asset de primera clase
El curso de prueba es seed presente en todo despliegue (is_demo), ejemplo para los dos
roles: el admin ve “esto se genera”, el empleado lo hace. Visible sin clave (ya generado).
Con clave, el mismo curso se vuelve conversable (tutor) y adaptable/regenerable — la clave
no cambia qué ves, cambia cuánto puedes hacer con ello.
3. Flujos (un patrón, dos rellenos)
Bifurca solo en el rol; cada rama es mínima. Estructura común: encuadre → una captura que importa → primera victoria.
3.1 Empleado (cuenta creada por el admin; nunca toca la clave)
- Login → home no-vacía (matriculado en el demo / sus cursos) + “empieza aquí” suave.
- Tour por defecto, cerrable: “abre tu primera lección” → toca una lección ya pre-generada y personalizada (instantánea, prewarm). Aha = rica y se adapta.
- Micro-captura opcional: “¿cómo te gusta que te expliquen? (opcional)” → presets + “otro” (el “otro” solo con clave). Saltable → perfil por defecto + inferencia por conducta.
- Empujón de contraste (ignorable): “así se lo explicamos a otra persona” → misma materia, distinta UI → capta la personalización.
- A partir de ahí, solo aprende.
3.2 Admin / dueño (auto-hospeda y monta la empresa)
- Setup mínimo (ya existe) + una pregunta de arquetipo (educación/empresa → defaults).
- Panel no-vacío con el curso demo + tour cerrable.
- Descubre la UI generativa tocando el demo (mismo contraste de dos personas).
- Momento “hazlo tuyo”: “Crea tu primer curso”. Si no hay clave → “conecta tu IA” (por qué + enlace al proveedor + pegar + validar en vivo). No es peaje: llegas aquí ya convencido. Mientras, todo explorable; acciones de IA muestran “conecta una clave” en su sitio.
- Generación real = en segundo plano + aviso. Nunca una barra de carga como bienvenida.
- Invita a empleados.
4. Plan por fases (barato → caro; nada complejo de primeras)
Motor del tour: react-joyride (spotlight, “paso 1 de N”, saltar/cerrar, control de pasos). Nosotros ponemos los datos y el estado (§2.3, §2.4), no el motor.
Estado (2026-08-21): Fase 0 ✅, la infra de Fase 2 (
Capabilities/<Gated>) ✅ y el banner de degradado ✅ están hechos y enmain; el tour de admin (Fase 1) también. Queda, con decisión/diseño de por medio: el 3-way de arquetipo +resolveDefaults, el empujón de contraste, la API key desde la UI, la generación en 2º plano, y la Fase 3.
Fase 0 — MVP ✅ HECHO
- home no-vacía con el demo (seed). ✅
- Tour joyride empleado (home → primera lección pre-generada), cerrable,
localStorage. ✅ - Perfil no bloqueante (
ONBOARDING_ENABLED+ captura existente). ✅ - Solo elementos estáticos. ✅
Fase 1 — hacer visible el diferenciador ◑ PARCIAL (resto: con diseño)
- ○ Empujón de contraste (dos personas, datos del seed) — pendiente.
- ✅ Tour de admin — hecho:
ProductTourrole-aware (mismo joyride, pasos admin). - ○ 1 smart-default: arquetipo empresa/educación →
resolveDefaults(empresa apaga la mascota) + la línea que lo explica — pendiente (necesita backend del arquetipo).
Fase 2 — el desbloqueo self-hosted (la capa de IA) ◑ PARCIAL
- ✅
Capabilities+useCapabilities()+<Gated>(§2.1–2.2) — infra hecha (backend deriva de las claves y lo expone en/setup/status; front la consume). - ✅ Banners de degradado (
CapabilityHealthBanneren admin) — hecho. - ○ API key desde la UI (pegar + validar) — pendiente (decisión de almacenamiento/seguridad).
- ○ Encender los
requires: “otro” libre, preview generado, chat con el tutor. - ○ Generación en segundo plano + aviso.
Fase 3 — lo complejo, al final ○ PENDIENTE
- Inferir el perfil del comportamiento (qué abre/relee/salta). Módulo separable de señales.
- Estado del onboarding cross-device, arquetipos más finos.
5. Qué ya existe vs. qué falta
| Ya existe (✅) | Falta (○) |
|---|---|
| Setup wizard + bienvenida (logo/mascota/degradado), login, home | Pregunta de arquetipo + resolveDefaults |
| Curso demo (seed) + lecciones pre-generadas (prewarm) | Empujón de contraste (dos personas) |
| Tour joyride role-aware (empleado + admin), reabrible, estado por rol | API key en UI (pegar+validar) — hoy en .env |
Captura de perfil + flag ONBOARDING_ENABLED |
Generación en 2º plano + requires (“otro”/preview/tutor) |
Capabilities + useCapabilities + <Gated> + banner de degradado |
Fase 3 (inferir perfil, cross-device) |
6. Resumen en una frase
Plataforma llena desde el inicio · guía por defecto pero siempre cerrable y reabrible · valor por tocar, no por contar · perfil ofrecido o inferido, nunca impuesto · defaults auto-elegidos y reversibles · la clave desbloquea, no cobra — todo bajo un motor limpio: la app declara qué necesita (capacidad × rol) y un resolver decide qué mostrar y cómo degradar.