# P01 — 🏋️ App Strength Tracker

> Modernización de la app web de gimnasio: normalización, PWA, persistencia, sincronización.
> Base: `plan_detallado_strengthapp.md` · Estado: 🔴 Crítica — Nos ponemos ahora

## Datos del proyecto

| Campo | Valor |
|-------|-------|
| **Prioridad** | 🔴 Crítica |
| **Estado** | ⏳ En Proceso |
| **Progreso** | 5% |
| **Duración estimada** | ~8 semanas |
| **Dependencias** | — |
| **Responsable** | Chew (ejecución) + Jorge (revisión, aprobación) |

---

### ⚡ FASE 0 — NORMALIZACIÓN (PREVIO A TODO)

| ID | Tarea | Prioridad | Estado | Fecha límite | Responsable | Criterios de Aceptación | Dependencias | Creado | Última acción | % |
|----|-------|-----------|--------|-------------|-------------|--------------------------|-------------|--------|--------------|---|
| **1.1** | **Unificación de nombres de ejercicios** — El catálogo tiene 127 nombres inconsistentes (spanglish, duplicados). Reducir a 53 IDs únicos agrupados por ejercicio+máquina. | 🔴 Crítica | ⏳ En Proceso | — | Chew + Jorge | Tabla `exercise_machine_map` con 53 IDs. Cada nombre de app se mapea a un ID canónico. Jorge revisa y aprueba. | — | 15/05 | Análisis completo y propuesta DB en `sport/propuesta-bbdd-estructura.md` | 70% |
| **1.2** | **Diseñar esquema SQL normalizado** — Tablas: exercises, machines, muscle_groups, exercise_aliases, workouts, workout_exercises, workout_sets | 🔴 Crítica | ⏳ Pendiente revisión | — | Chew | Esquema documentado con PKs, FKs, índices. Validado que cubre todos los datos actuales. | 1.1 | 15/05 | Propuesta detallada en `sport/propuesta-bbdd-estructura.md` | 50% |
| **1.2** | **Diseñar esquema SQL normalizado** — Tablas: exercises, machines, muscle_groups, exercise_machine_map, workouts, workout_exercises, workout_sets | 🔴 Crítica | ⏳ Pendiente | — | Chew | Esquema documentado con PKs, FKs, índices. Validado que cubre todos los datos actuales. | 1.1 | 15/05 | — | 0% |
| **1.3** | **Poblar tablas del catálogo** — Volcar los 53 ejercicios + máquinas + grupos musculares al nuevo esquema | 🔴 Crítica | ⏳ Pendiente | — | Chew | `SELECT * FROM exercises` → 41-53 filas con datos completos | 1.1, 1.2 | 15/05 | — | 0% |
| **1.4** | **Script de migración de datos históricos** — Mapear nombres antiguos → IDs y reescribir en nuevo esquema | 🔴 Alta | ⏳ Pendiente | — | Chew | 100% de los sets históricos migrados. 0 sets con nombre en texto suelto. | 1.3 | 15/05 | — | 0% |

### 📱 FASE 1 — PWA

| ID | Tarea | Prioridad | Estado | Fecha límite | Responsable | Criterios de Aceptación | Dependencias | Creado | Última acción | % |
|----|-------|-----------|--------|-------------|-------------|--------------------------|-------------|--------|--------------|---|
| **1.5** | **Crear manifest.json** con name, short_name, icons, theme-color, display standalone | 🔴 Alta | ⏳ Pendiente | — | Chew | Lighthouse → PWA badge, manifest se parsea sin errores | — | 15/05 | — | 0% |
| **1.6** | **Generar icons PWA** (192px, 512px) en PNG con fondo del theme-color | 🔴 Alta | ⏳ Pendiente | — | Chew | Archivos en `www/assets/icons/`, cargan en DevTools | — | 15/05 | — | 0% |
| **1.7** | **Instalar e integrar Workbox v7** para precaching de assets | 🔴 Alta | ⏳ Pendiente | — | Chew | `npm ls workbox-*` muestra dependencias | — | 15/05 | — | 0% |
| **1.8** | **Implementar service worker** con precaching + runtime cache | 🔴 Alta | ⏳ Pendiente | — | Chew | SW "activated and running", requests servidas desde cache | 1.7 | 15/05 | — | 0% |
| **1.9** | **Configurar Capacitor PWA** y sincronizar | 🔴 Alta | ⏳ Pendiente | — | Chew | `npx cap sync` sin errores | 1.5, 1.8 | 15/05 | — | 0% |
| **1.10** | **Auditar con Lighthouse** (target PWA 90+) | 🟡 Media | ⏳ Pendiente | — | Chew | Lighthouse PWA category pass | 1.9 | 15/05 | — | 0% |
| **1.11** | **Test de instalación en Android** como standalone offline | 🟡 Media | ⏳ Pendiente | — | Jorge | App abre sin barra direcciones, funciona offline | 1.10 | 15/05 | — | 0% |
| **1.12** | **Verificar compatibilidad Android nativo** — `npx cap sync`, build | 🟡 Media | ⏳ Pendiente | — | Chew | App nativa arranca, todas las vistas ok | 1.11 | 15/05 | — | 0% |

### 💾 FASE 2 — PERSISTENCIA ROBUSTA

| ID | Tarea | Prioridad | Estado | Fecha límite | Responsable | Criterios de Aceptación | Dependencias | Creado | Última acción | % |
|----|-------|-----------|--------|-------------|-------------|--------------------------|-------------|--------|--------------|---|
| **1.13** | **Diseñar interfaz StorageAdapter** | 🔴 Alta | ⏳ Pendiente | — | Chew | Interface con get/set/query/transaction/migrate, test en memoria | 1.4 | 15/05 | — | 0% |
| **1.14** | **Implementar adaptador IndexedDB** vía `idb` wrapper | 🔴 Alta | ⏳ Pendiente | — | Chew | Escribir 100 sets, leer por rango, cerrar/reabrir → datos intactos | 1.13 | 15/05 | — | 0% |
| **1.15** | **Migrar datos de localStorage a IndexedDB** (script one-shot) | 🔴 Alta | ⏳ Pendiente | — | Chew | Datos visibles en DevTools, localStorage marcado como migrated | 1.14 | 15/05 | — | 0% |
| **1.16** | **Verificar persistencia IndexedDB** — cierre y reapertura | 🟡 Media | ⏳ Pendiente | — | Chew | Ctrl+Shift+R → datos siguen ahí | 1.15 | 15/05 | — | 0% |
| **1.17** | **Instalar y configurar `@capacitor-community/sqlite`** | 🔴 Alta | ⏳ Pendiente | — | Chew | `npx cap sync` sin errores, plugin disponible | — | 15/05 | — | 0% |
| **1.18** | **Implementar adaptador SQLite para Android** | 🔴 Alta | ⏳ Pendiente | — | Chew | Escribir 100 sets en Android, reiniciar, leer → datos intactos | 1.13, 1.17 | 15/05 | — | 0% |
| **1.19** | **Implementar detección de plataforma** (factory) | 🟡 Media | ⏳ Pendiente | — | Chew | Web → IndexedDB, Android → SQLite | 1.14, 1.18 | 15/05 | — | 0% |
| **1.20** | **Backup automático post-migración** | 🟢 Baja | ⏳ Pendiente | — | Chew | Se genera `backup-{fecha}.json` descargable | 1.15 | 15/05 | — | 0% |

### 🔄 FASE 3 — SINCRONIZACIÓN OFFLINE-FIRST

| ID | Tarea | Prioridad | Estado | Fecha límite | Responsable | Criterios de Aceptación | Dependencias | Creado | Última acción | % |
|----|-------|-----------|--------|-------------|-------------|--------------------------|-------------|--------|--------------|---|
| **1.21** | **Export automático al completar sesión** (JSON con metadatos) | 🟡 Media | ⏳ Pendiente | — | Chew | Completar sesión → se crea `sync-{fecha}.json` | 1.4 | 15/05 | — | 0% |
| **1.22** | **Implementar Sync Watcher en web** (File System API / drag-drop) | 🟡 Media | ⏳ Pendiente | — | Chew | Dropear sync JSON → detectado y mostrado | 1.21 | 15/05 | — | 0% |
| **1.23** | **Reforzar algoritmo mergeWeeks** (conservador, sin pérdidas) | 🟡 Media | ⏳ Pendiente | — | Chew | Test: mismo set → no duplica; nuevo → añade; modificado → actualiza si más reciente | 1.14 | 15/05 | — | 0% |
| **1.24** | **UI de sincronización** (botón, estado, última sync, diff preview) | 🟡 Media | ⏳ Pendiente | — | Chew | "Última sync: hace 3h" → clic → "2 entrenamientos nuevos" | 1.22, 1.23 | 15/05 | — | 0% |
| **1.25** | **Integrar Google Drive API como puente** Android↔Web | 🟡 Media | ⏳ Pendiente | — | Chew | "Sync a Drive" → archivo subido; "Sync desde Drive" → descargado y mergeado | 1.24 | 15/05 | — | 0% |
| **1.26** | **OAuth login para Google Drive** (flujo primera vez + refresh) | 🟡 Media | ⏳ Pendiente | — | Chew | Login → autorizar → token guardado; segunda vez → no pide login | 1.25 | 15/05 | — | 0% |
| **1.27** | **Test E2E: Android → Web** (entrenar, export, import) | 🟡 Media | ⏳ Pendiente | — | Jorge | 3 sets nuevos en Android → aparecen en histórico web | 1.26 | 15/05 | — | 0% |
| **1.28** | **Test E2E bidireccional** (Web → Android) | 🟡 Media | ⏳ Pendiente | — | Jorge | Flujo inverso → datos viajan sin pérdida | 1.27 | 15/05 | — | 0% |

### 🧩 FASE 4 — REFACTOR A MÓDULOS ES

| ID | Tarea | Prioridad | Estado | Fecha límite | Responsable | Criterios de Aceptación | Dependencias | Creado | Última acción | % |
|----|-------|-----------|--------|-------------|-------------|--------------------------|-------------|--------|--------------|---|
| **1.29** | **Extraer logic.js** (lógica de entrenamiento: series, RIR, descanso) | 🟡 Media | ⏳ Pendiente | — | Chew | `import { calculateSet }` → funciona, app renderiza igual | — | 15/05 | — | 0% |
| **1.30** | **Extraer analytics.js** (1RM, progresión, estadísticas) | 🟡 Media | ⏳ Pendiente | — | Chew | `import { estimate1RM }` → gráficos y tablas igual que antes | — | 15/05 | — | 0% |
| **1.31** | **Extraer ui.js** (DOM, vistas, eventos) | 🟡 Media | ⏳ Pendiente | — | Chew | App visualmente idéntica, todas las interacciones funcionan | — | 15/05 | — | 0% |
| **1.32** | **Extraer storage.js** (StorageAdapter + persistencia) | 🟡 Media | ⏳ Pendiente | — | Chew | `import { StorageAdapterFactory }` → datos correctos | 1.13-1.19 | 15/05 | — | 0% |
| **1.33** | **Extraer sync.js** (sincronización, mergeWeeks, Drive) | 🟡 Media | ⏳ Pendiente | — | Chew | `import { syncToDrive }` → sync funcional | 1.21-1.26 | 15/05 | — | 0% |
| **1.34** | **Crear entry point app.js** con ES module imports | 🟡 Media | ⏳ Pendiente | — | Chew | `index.html` solo carga `<script type="module" src="js/app.js">` | 1.29-1.33 | 15/05 | — | 0% |
| **1.35** | **Extraer CSS** — todo el CSS a `css/app.css` | 🟢 Baja | ⏳ Pendiente | — | Chew | `index.html` sin `<style>`, apariencia idéntica | — | 15/05 | — | 0% |
| **1.36** | **Test de regresión completo** | 🟡 Media | ⏳ Pendiente | — | Jorge | Entrenamiento completo sin errores en consola | 1.34, 1.35 | 15/05 | — | 0% |

### 🧪 FASE 5 — TESTS + CI + PULIDO

| ID | Tarea | Prioridad | Estado | Fecha límite | Responsable | Criterios de Aceptación | Dependencias | Creado | Última acción | % |
|----|-------|-----------|--------|-------------|-------------|--------------------------|-------------|--------|--------------|---|
| **1.37** | **Tests unitarios para logic.js** (calculateSet, RIR, descanso) | 🟡 Media | ⏳ Pendiente | — | Chew | `npx vitest run` → 10+ tests: normales + bordes (RIR=0, peso=0) | 1.29 | 15/05 | — | 0% |
| **1.38** | **Tests unitarios para analytics.js** (1RM, progresión, volumen) | 🟡 Media | ⏳ Pendiente | — | Chew | 8+ tests: Epley, Brzycki, progresión semanal | 1.30 | 15/05 | — | 0% |
| **1.39** | **Tests de validación del esquema de datos** | 🟡 Media | ⏳ Pendiente | — | Chew | Insert válido → ok; sin FK → error; tipos incorrectos → error | 1.4 | 15/05 | — | 0% |
| **1.40** | **Configurar GitHub Actions** (ci → lint + test + build) | 🟢 Baja | ⏳ Pendiente | — | Chew | Push a main → Actions corre en <3min, badge README | 1.37-1.39 | 15/05 | — | 0% |
| **1.41** | **Lazy loading de Chart.js** (solo en vista analíticas) | 🟢 Baja | ⏳ Pendiente | — | Chew | Vista entrenamiento → Chart.js NO en bundle | 1.34 | 15/05 | — | 0% |
| **1.42** | **Critical CSS inline** para mejorar FCP | 🟢 Baja | ⏳ Pendiente | — | Chew | Lighthouse → render-blocking resources mejorado | 1.35 | 15/05 | — | 0% |
| **1.43** | **Documentación técnica** (README, setup, comandos) | 🟢 Baja | ⏳ Pendiente | — | Chew | README existe, cualquiera puede clonar y ejecutar | 1.40 | 15/05 | — | 0% |

---

## 📊 HITOS GLOBALES

| Hito | Criterio | Tareas asociadas |
|------|----------|-----------------|
| **H1 — Catálogo normalizado** | 53 IDs únicos, tabla de mapeo aprobada, esquema diseñado | 1.1 → 1.3 |
| **H2 — PWA instalable** | Lighthouse pass, offline standalone en Android | 1.5 → 1.12 |
| **H3 — Datos migrados** | localStorage → IndexedDB/SQLite, backup automático | 1.13 → 1.20 |
| **H4 — Sync funcional** | Android↔Web bidireccional, automático, sin pérdidas | 1.21 → 1.28 |
| **H5 — Código modular** | 6 módulos ES, CSS externo, bundle optimizado | 1.29 → 1.36 |
| **H6 — Calidad** | Tests en CI, lazy loading, documentado | 1.37 → 1.43 |

---

**Total: 43 tareas** · Creado: 15/05/2026 · Siguiente paso: **1.1 — unificación de nombres** 🔴
