Global States & System States
Cómo se comporta la app en cada estado
Propósito: Definir UI/UX en loading, error, empty, offline
Impacto: Crítico para user experience
Alcance: Todos los screens y componentes
Global States (4 Niveles)
STATE 1: LOADING
Cuándo: Primera carga, fetch de datos, procesamiento
Visual:
- Skeleton shimmer (500ms primera carga, 200ms subsecuente)
- Placeholder shapes que coinciden con layout
- Pulse animation sutil
- NO spinner genérico
Duración:
- Máximo 800ms (después user se aburre)
- Si toma >2s, mostrar “Cargando datos…” con contador
Copy:
- “Cargando tu salud…”
- “Procesando análisis…”
- “Sincronizando datos…”
Ejemplo (Dashboard):
(shimmer header) ← Skeleton logo
(shimmer h1) ← Skeleton greeting
(shimmer subtitle)
(card) ← Hero card skeleton
(80%)
← Feature cards
(50%) (30%)
STATE 2: EMPTY
Cuándo: No hay datos (ej: sin check-ins, sin alerts, onboarding pending)
Visual:
- Ilustración minimalista (200x200px)
- Copy claro y empático
- CTA primary button
Pattern:
[Ilustración] ← 200x200px SVG
Sin datos aún ← h3
Completa onboarding para ver ← body
tu salud personalizada
[IR A ONBOARDING] ← CTA
¿Preguntas? help@aden.run ← Help link
Empty States Específicos:
| Screen | Mensaje |
|---|---|
| Alertas | “No hay alertas. ¡Vas muy bien!” |
| Exámenes | “Carga tus laboratorios para empezar” |
| Check-ins Histórico | “Haz tu primer check-in hoy” |
| Chat (primer uso) | “¿Preguntas? Chat con Health Coach →” |
STATE 3: ERROR
Cuándo: Fallo de conexión, API error, validación falla
Visual:
- Color rojo (#ef4444)
- Icono de error (AlertCircleIcon)
- Mensaje claro (no código técnico)
- CTA: “Reintentar” o “Contactar soporte”
Niveles de Error:
ERROR 1: Validation Error (Local)
Input error (ej: email inválido):
Email inválido ← Label
[tu@email.com] ← Input con border rojo
Formato incorrecto ← Error message
ERROR 2: Network Error (API 4xx/5xx)
Fallo al cargar dashboard:
[Icono Error]
Algo salió mal
No pudimos cargar tu salud.
Verifica tu conexión e
intenta de nuevo.
[REINTENTAR] [CONTACTAR]
ERROR 3: Timeout (>10 segundos)
Está tardando más de lo usual
[REINTENTAR] [CONTACTAR]
Copy por Error Code:
| Code | User Message |
|---|---|
| 401 | “Tu sesión expiró. Inicia sesión de nuevo” |
| 403 | “No tienes permiso para ver esto” |
| 404 | “No encontramos lo que buscas” |
| 500 | “Nuestro servidor tiene problemas. Reintentar” |
| Network | “Sin conexión. Verifica tu internet” |
STATE 4: OFFLINE
Cuándo: Sin conexión a internet
Visual:
- Banner superior: “Sin conexión” (amarillo/gris)
- App funciona en read-only (cached data)
- No permitir cambios (botones disabled)
Banner:
Sin conexión a internet
Estás viendo datos en caché.
Algunos datos podrían estar
desactualizados.
[INTENTAR RECONECTAR]
Behavior:
- Read all screens (cached)
- Create/Update/Delete (disabled)
- Submit forms (disabled)
- See last sync timestamp (“Actualizado hace 30 min”)
Example (Offline Check-in):
Check-in Modal
¿Tomaste suplementos?
Sí No
[GUARDAR OFFLINE] ← Disabled (grayed)
Cuando reconectes, se sincronizará ← Hint text
[DESCARTAR] ← Enabled
Component States
Button States
NORMAL:
[PRIMARY BUTTON]
HOVER:
[PRIMARY BUTTON] ← slight shadow, translateY(-1px)
ACTIVE/PRESS:
[PRIMARY BUTTON] ← darker, translateY(0px)
DISABLED:
[PRIMARY BUTTON] ← opacity 50%, cursor: not-allowed
LOADING:
[ GUARDAR...] ← spinner, disabled
Input States
NORMAL:
[Input field] ← border gray, bg white
FOCUS:
[Input field] ← border blue, ring blue
FILLED:
[Input field] ← border gray, text present
ERROR:
[Input field] ← border red, bg light red
Error message below
DISABLED:
[Input field] ← bg gray, cursor: not-allowed, opacity 50%
Card States
NORMAL:
Card Content
HOVER (if clickable):
Card Content ← shadow increases, translateY(-2px)
LOADING (skeleton):
(shimmer)
ERROR:
Error
Algo salió mal
[REINTENTAR]
Transitions & Timing
| Element | Duration | Easing |
|---|---|---|
| Skeleton shimmer | 500ms | infinite |
| FadeInUp (content) | 260ms | cubic-bezier(0.4, 0, 0.2, 1) |
| Button hover | 150ms | ease-out |
| Modal enter | 200ms | ease-out |
| Skeleton → content | 300ms | fade |
| Error message | 200ms | fade-in |
Offline-First Strategy
Principle: App works offline, syncs when reconnected
USER ACTION (offline):
1. Action queued (localStorage)
2. UI feedback: "Guardado offline"
3. Badge appears: "1 cambio pendiente"
RECONNECT:
1. Detect connection restored
2. Sync queue with backend
3. Badge disappears
4. Success notification: "Sincronizado"
IF SYNC FAILS:
1. Retry logic (exponential backoff)
2. User can manual retry
3. Error message with "Contactar soporte"
Timeout & Retry Logic
REQUEST SENT
↓ (3 segundos)
SIN RESPUESTA → Mostrar "Cargando..."
↓ (7 segundos más)
TIMEOUT → Error: "Tardando más de lo usual" + Retry btn
↓ (después de 3 reintentos fallidos)
ABORT → Fatal error + Contact support
Success States
Check-in Completo
¡Excelente, María!
Mantén el ritmo
Streak: 23 días
Próximo: mañana a las 8am
Plan Guardado
Cambios guardados
Tus nuevas recomendaciones
están listas
Labs Cargados
Exámenes procesándose...
Recibirás notificación cuando
esté listo (30 minutos)
Próximo: edge-cases — Special Situations & Validations