HELIOS DECK — Roadmap de Desarrollo
8 sprints: del primer fetch al deploy asistido por IA
Visión general
- 8 Sprints · ~2 semanas/sprint · 8 APIs integradas · ∞ widgets posibles
Cada sprint produce entregables funcionales que se integran progresivamente. El proyecto crece de un simple fetch a un dashboard completo con autenticación, widgets configurables y deploy asistido por IA.
Sprint 1 — Foundation & Data Sources
Inicialización del proyecto fullstack con React Router v7 SSR, configuración de Tailwind v4, SQLite con Drizzle ORM, y el primer data fetcher funcional (NASA DONKI).
Entregables: Proyecto RR v7 SSR + Vite funcionando · Tailwind v4 + Shadcn configurados · SQLite + Drizzle con esquema inicial · API Registry JSON con las 8 fuentes · Primer fetcher: NASA DONKI (flares) · Loader que lee de SQLite y muestra datos · Estructura de carpetas definida · README con instrucciones de setup
Tech: React Router v7 · Vite · Tailwind v4 · better-sqlite3 · Drizzle ORM · JavaScript
Detalle del Sprint 1
Día 1-2: Scaffolding
npx create-react-router@latest helios-deck --template remix-run/react-router/templates/node
cd helios-deck
npm install better-sqlite3 drizzle-orm
npm install -D drizzle-kit @types/better-sqlite3
npm install @tanstack/react-query tailwindcss @tailwindcss/vite
Día 3-4: Esquema DB + primer fetcher
- Crear
app/db/schema.jscon tablasignals - Implementar
app/services/fetchers/nasa-donki.js - Obtener API key gratuita en api.nasa.gov
- Primer
loaderenapp/routes/signals/solar-activity.jsx
Día 5: Verificación end-to-end
- Ejecutar fetcher → normalizar → insertar en SQLite → leer en loader → renderizar
- Commit:
feat: initial setup with NASA DONKI solar flare fetcher
Sprint 2 — CRUD & Data Normalization
Implementación del servicio HttpRequest centralizado, HttpErrors para mensajes genéricos, pipeline de normalización (validación en runtime), y CRUD completo para configuraciones de fuentes de datos.
Entregables: HttpRequest service (GET/POST/PUT/DELETE) · HttpErrors con mensajes por status code · Normalizer con validación en runtime · Todos los 8 fetchers implementados · Action CRUD para gestión de fuentes · Aggregator que ejecuta todos los fetchers · Componentes Loader y ErrorBoundary · useNotifications hook global
Tech: TanStack Query · HttpRequest · HttpErrors · Promise.allSettled
Detalle del Sprint 2
Servicio HttpRequest:
// app/services/HttpRequest.js
const DEFAULT_TIMEOUT = 10_000;
export class HttpRequest {
static async get(url, options) {
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), DEFAULT_TIMEOUT);
try {
const res = await fetch(url, { ...options, signal: controller.signal });
if (!res.ok) throw new HttpError(res.status, res.statusText);
return res.json();
} finally {
clearTimeout(timeout);
}
}
static async post(url, body) {
const res = await fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(body),
});
if (!res.ok) throw new HttpError(res.status, res.statusText);
return res.json();
}
// PUT y DELETE siguen el mismo patrón
}
Pipeline de normalización:
API Response (heterogéneo)
│
▼
Fetcher.fetch() ← Transforma respuesta cruda a array
│
▼
normalize(raw) ← Aplica forma normalizada, timestamps ISO, units
│
▼
db.insert(signals) ← Persiste en SQLite con UNIQUE constraint
Sprint 3 — Visualización & Charts
Integración de librerías de visualización (Recharts / D3). Cada estudiante construye un widget visual para su señal asignada: gráficas temporales, gauges, feeds de eventos.
Entregables: TimeChart component (Recharts) · Gauge component para Kp y aurora · EventFeed para erupciones solares · SignalCard con valor actual + tendencia · Rango temporal configurable (6h/24h/7d) · Responsive y dark mode en todos los charts · Tooltips interactivos con datos de fuente · Loading skeletons para cada widget
Tech: Recharts · D3 (opcional) · Framer Motion · Skeleton UI
Tipos de widget por señal
| Señal | Widget | Visualización |
|---|---|---|
solar_flare_events |
EventFeed | Lista cronológica con badges de clase (C/M/X) |
coronal_mass_ejections |
TimeChart | Scatter plot con velocidad de eyección |
solar_wind_speed |
TimeChart | Línea temporal con umbral de tormenta |
solar_wind_density |
TimeChart | Área apilada (speed + density) |
kp_index |
Gauge | Medidor circular 0–9 con colores semáforo |
auroral_oval_probability |
Gauge | Probabilidad % con gradiente verde-púrpura |
iss_coordinates |
ISSMap | Mapa Leaflet (se completa en Sprint 4) |
solar_radiation |
TimeChart | Barras diarias con media móvil |
Sprint 4 — Real-Time & WebSockets
Servidor WebSocket integrado en el proceso Node.js. Canales para posición ISS en vivo y viento solar. Hook useWebSocket con reconexión automática y heartbeat.
Entregables: WebSocket server con canales · Canal iss:position (cada 5s) · Canal solar:wind (real-time) · useWebSocket hook con reconexión · ISSMap con Leaflet + trayectoria viva · Indicador de conexión WS en UI · Heartbeat + detección de desconexión · Notificaciones en vivo (tormentas solares)
Tech: ws (WebSocket) · Leaflet · react-leaflet · Exponential backoff
ISSMap: ejemplo de widget en tiempo real
// app/components/widgets/ISSMap.jsx
import { MapContainer, TileLayer, Marker, Polyline } from 'react-leaflet';
import { useWebSocket } from '~/hooks/useWebSocket';
import { useState, useEffect } from 'react';
export function ISSMap() {
const { data, status } = useWebSocket('iss:position');
const [trail, setTrail] = useState<[number, number][]>([]);
useEffect(() => {
if (data) {
setTrail(prev => [...prev.slice(-50), [data.latitude, data.longitude]]);
}
}, [data]);
return (
<div className="relative">
{status !== 'open' && (
<div className="absolute top-2 right-2 z-[1000] px-2 py-1 bg-yellow-500/90 text-xs rounded">
Reconectando...
</div>
)}
<MapContainer
center={data ? [data.latitude, data.longitude] : [0, 0]}
zoom={3}
className="h-64 w-full rounded-lg"
>
<TileLayer
url="https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png"
attribution="CARTO"
/>
{data && <Marker position={[data.latitude, data.longitude]} />}
{trail.length > 1 && (
<Polyline positions={trail} color="#60a5fa" weight={2} opacity={0.6} />
)}
</MapContainer>
</div>
);
}
Sprint 5 — Widget System & Dashboard
Arquitectura de widgets configurables. Grid drag-and-drop con react-grid-layout. Widget registry, props configurables por widget y persistencia de layout en SQLite.
Entregables: Widget Registry (JSON de tipos disponibles) · DashboardGrid con react-grid-layout · WidgetConfigModal (CRUD de props) · Drag & drop para reordenar widgets · Resize de widgets en el grid · Persistencia de layout en SQLite · Widget factory pattern (render por tipo) · Añadir/eliminar widgets del dashboard
Tech: react-grid-layout · Widget Registry · Factory Pattern · Form actions (CRUD)
Widget Registry
// app/lib/widget-registry.js
export const WIDGET_REGISTRY = {
time_chart: {
label: 'Gráfica Temporal',
component: 'TimeChart',
defaultSize: { w: 6, h: 4 },
configSchema: {
signal: { type: 'select', options: ['solar_wind_speed', 'solar_radiation', ...] },
timeRange: { type: 'select', options: ['6h', '24h', '7d', '30d'] },
chartType: { type: 'select', options: ['line', 'area', 'bar'] },
},
},
gauge: {
label: 'Medidor',
component: 'Gauge',
defaultSize: { w: 3, h: 3 },
configSchema: {
signal: { type: 'select', options: ['kp_index', 'auroral_oval_probability'] },
thresholds: { type: 'json' },
},
},
map: {
label: 'Mapa ISS',
component: 'ISSMap',
defaultSize: { w: 6, h: 5 },
configSchema: {
showTrail: { type: 'boolean', default: true },
trailLength: { type: 'number', default: 50 },
},
},
event_feed: {
label: 'Feed de Eventos',
component: 'EventFeed',
defaultSize: { w: 4, h: 5 },
configSchema: {
signal: { type: 'select', options: ['solar_flare_events', 'coronal_mass_ejections'] },
limit: { type: 'number', default: 20 },
},
},
signal_card: {
label: 'Tarjeta de Señal',
component: 'SignalCard',
defaultSize: { w: 3, h: 2 },
configSchema: {
signal: { type: 'select', options: ['*'] },
showTrend: { type: 'boolean', default: true },
},
},
} as const;
Flujo CRUD del dashboard
Usuario abre /dashboard
│
▼
loader() → SELECT dashboards + widgets WHERE user_id = ?
│
▼
DashboardGrid renderiza widgets según layout JSON
│
├── [Drag] → onLayoutChange → Form submit → action(intent:'update')
├── [+ Add] → WidgetConfigModal → Form submit → action(intent:'create')
└── [✕ Remove] → Confirm → Form submit → action(intent:'delete')
│
▼
action() → INSERT/UPDATE/DELETE en SQLite → redirect('/dashboard')
Sprint 6 — Autenticación & User Profiles
Sistema de autenticación basado en cookies con SQLite como store de usuarios. Registro, login, protección de rutas y persistencia de dashboards por usuario.
Entregables: Formulario de registro con validación · Formulario de login con sesión cookie · requireUser middleware en loaders · Hash de contraseñas con bcrypt · Página de perfil de usuario · Dashboard por defecto para nuevos usuarios · Protección de rutas privadas · Logout con destrucción de sesión
Tech: bcryptjs · Cookie Sessions · createCookieSessionStorage
Flujo de autenticación
GET /auth/register
│
▼
Form → POST /auth/register
│
▼
action() {
1. Validar email + password
2. Verificar que email no exista
3. Hash password con bcrypt
4. INSERT INTO users (email, password_hash)
5. Crear dashboard por defecto
6. Crear sesión cookie → redirect('/dashboard')
}
│
▼
GET /dashboard (loader verifica sesión → sirve datos del usuario)
Sprint 7 — AI-Assisted Development & DevOps
Creación de rules y skills para asistentes de IA (.cursor, Claude Code, Windsurf, Antigravity). Documentación como definición para deploy asistido. CI/CD pipeline y estrategia de despliegue.
Entregables: .cursor/rules/ para convenciones del proyecto · .cursor/skills/ para workflows de desarrollo · CLAUDE.md para Claude Code · Docs-as-deployment-definition · Dockerfile multi-stage optimizado · GitHub Actions CI pipeline · Deploy a Fly.io con SQLite volume · Health check endpoint + monitoring
Tech: .cursor/rules · CLAUDE.md · Docker · GitHub Actions · Fly.io
Ecosistema de asistentes IA soportados
- Cursor — Rules en
.cursor/rules/para convenciones de código, estructura de carpetas y patrones del proyecto. Skills en.cursor/skills/para workflows de fetcher, widget y deploy. - Claude Code —
CLAUDE.mden la raíz con contexto del proyecto, stack técnico, convenciones y comandos frecuentes.AGENTS.mdpara delegación de tareas. - Windsurf —
.windsurfrulescon directivas de estilo y patrones de React Router v7. - Antigravity — Docs-as-definition: la documentación del proyecto (este track) sirve como entrada para generación asistida de código y despliegue automatizado.
Estructura de archivos AI-config
helios-deck/
├── .cursor/
│ ├── rules/
│ │ ├── project-conventions.mdc ← Estructura, naming, imports
│ │ ├── react-router-patterns.mdc ← Loader/action patterns
│ │ ├── database-patterns.mdc ← Drizzle ORM conventions
│ │ └── api-fetcher-pattern.mdc ← Contrato DataFetcher
│ └── skills/
│ ├── create-fetcher/SKILL.md ← Workflow: crear un nuevo fetcher
│ ├── create-widget/SKILL.md ← Workflow: crear un nuevo widget
│ └── deploy/SKILL.md ← Workflow: build + deploy a Fly.io
│
├── CLAUDE.md ← Contexto para Claude Code
├── AGENTS.md ← Delegación de tareas para agentes
├── .windsurfrules ← Convenciones para Windsurf
│
├── docs/
│ ├── architecture.md ← Este documento de arquitectura
│ ├── api-registry.json ← Registro de fuentes de datos
│ └── deployment.md ← Guía de deploy (docs-as-code)
│
└── .github/
└── workflows/
└── deploy.yml ← CI/CD con GitHub Actions
Ejemplo: .cursor rule para fetchers
---
description: Patrón para crear nuevos data fetchers
globs: ["app/services/fetchers/*.js"]
---
Todos los fetchers implementan la interfaz DataFetcher:
- Exportar una instancia nombrada (e.g. `nasaDonkiFetcher`)
- Método `fetch()` retorna una Promise que resuelve a un array de señales normalizadas
- Método `healthCheck()` retorna una Promise que resuelve a boolean
- Usar HttpRequest.get() con timeout configurable
- Validar forma de la respuesta en runtime
- Incluir retry con exponential backoff (3 intentos, base 1s)
- Logging estructurado en formato JSON
GitHub Actions: CI/CD
# .github/workflows/deploy.yml
name: Deploy HELIOS DECK
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
- run: npm ci
- run: npm run typecheck
- run: npm run lint
- run: npm test
- run: npm run build
- uses: superfly/flyctl-actions/setup-flyctl@master
- run: flyctl deploy --remote-only
env:
FLY_API_TOKEN: $
Sprint 8 — Polish, Testing & Launch
Optimización de rendimiento, auditoría de accesibilidad, testing end-to-end, últimos ajustes de UX y lanzamiento público del observatorio cósmico.
Entregables: Performance audit (Lighthouse > 90) · Accesibilidad WCAG AA verificada · Tests unitarios para fetchers y normalizer · Tests E2E para flujos críticos (Playwright) · Error monitoring (Sentry o similar) · SEO meta tags + Open Graph · Landing page pública del proyecto · Presentación final y retrospectiva
Tech: Vitest · Playwright · Lighthouse · Sentry · axe-core
Checklist de lanzamiento
Pre-launch:
✓ Todos los fetchers con healthCheck pasando
✓ Dashboard funcional con al menos 4 widgets
✓ WebSocket estable con reconexión verificada
✓ Auth: registro, login, logout, sesiones persistentes
✓ CRUD de widgets sin errores
✓ Responsive verificado en móvil, tablet, desktop
✓ Dark mode consistente en todos los componentes
✓ Loading states en todas las rutas
✓ Error boundaries para fetch failures
✓ Lighthouse Performance > 90
✓ Lighthouse Accessibility > 90
✓ Tests pasando en CI
Launch:
→ Deploy a producción (Fly.io)
→ Verificar SQLite volume persistente
→ Health check endpoint respondiendo
→ WebSocket funcionando en producción
→ DNS / dominio configurado (si aplica)
→ Monitoring activo
Mapa visual de dependencias
Sprint 1 ──────┐
Foundation │
▼
Sprint 2 ──── Sprint 3 ──── Sprint 4
CRUD/Norm Charts WebSockets
│ │
└──────┬───────┘
▼
Sprint 5
Widget System
│
▼
Sprint 6
Auth & Users
│
▼
Sprint 7
AI Dev & DevOps
│
▼
Sprint 8
Polish & Launch
Los Sprints 2, 3 y 4 pueden ejecutarse en paralelo por diferentes estudiantes. Sprint 5 necesita los tres anteriores. Sprint 6 necesita Sprint 5. Sprints 7 y 8 son secuenciales.
Metodología ATELIER aplicada
| Fase ATELIER | Sprints | Actividad |
|---|---|---|
| Exploración | 1–2 | Investigar APIs, diseñar esquema, primer fetch funcional |
| Conceptualización | 3–4 | Diseñar widgets, planificar WebSocket, wireframes del dashboard |
| Producción | 5–6 | Construir widget system, auth, CRUD completo |
| Exhibición | 7 | Deploy, documentación, demo ante la clase |
| Reflexión | 8 | Testing, retrospectiva, mejoras y portfolio |
Evaluación por sprint
| Criterio | Peso | Sprint de evaluación |
|---|---|---|
| Fetching funcional con datos reales | 15% | Sprint 2 |
| Visualización con charts interactivos | 15% | Sprint 3 |
| WebSocket con datos en vivo | 10% | Sprint 4 |
| Widget system configurable | 15% | Sprint 5 |
| Auth + CRUD de dashboards | 15% | Sprint 6 |
| AI config + deploy | 10% | Sprint 7 |
| Testing + accesibilidad + performance | 10% | Sprint 8 |
| Git profesional + colaboración | 10% | Continuo |