miinventario-v2/orchestration/_archive/analisis/ANALISIS-DETALLADO-UX-MOBILE-2026-01-12.md
Adrian Flores Cortes e101c7b94c
Some checks are pending
Build / Build Backend (push) Waiting to run
Build / Build Docker Image (push) Blocked by required conditions
Build / Build Mobile (TypeScript Check) (push) Waiting to run
Lint / Lint Backend (push) Waiting to run
Lint / Lint Mobile (push) Waiting to run
Test / Backend E2E Tests (push) Waiting to run
Test / Mobile Unit Tests (push) Waiting to run
[ESTANDAR-ORCHESTRATION] refactor: Consolidate to standard structure
- Move 5 non-standard folders to _archive/
- Archive 2 extra root files
- Update _MAP.md with standardized structure

Standard: SIMCO-ESTANDAR-ORCHESTRATION v1.0.0
Level: CONSUMER (L2)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-24 14:38:05 -06:00

16 KiB

ANÁLISIS DETALLADO - UX MOBILE: Animaciones + Modo Offline


id: ANALISIS-DET-UX-MOBILE-001 type: DetailedAnalysis status: Approved created_date: 2026-01-12 updated_date: 2026-01-12 phase: 2-detallado simco_version: "4.0.0"

FASE 2: ANÁLISIS DETALLADO DE CAMBIOS

2.1 Archivos Nuevos - Análisis Completo


2.1.1 src/hooks/useAnimations.ts

Propósito: Colección de hooks reutilizables para animaciones con react-native-reanimated

Líneas de Código: 187 Dependencias Externas: react-native-reanimated Exports:

Export Tipo Parámetros Retorno Uso
useFadeIn Hook delay?: number { animatedStyle, opacity } Fade in de 0 a 1
useSlideIn Hook delay?: number, distance?: number { animatedStyle, opacity, translateY } Slide desde abajo
useSlideFromRight Hook delay?: number, distance?: number { animatedStyle, opacity, translateX } Slide desde derecha
usePressScale Hook pressedScale?: number { animatedStyle, onPressIn, onPressOut, scale } Efecto press en botones
useListItemAnimation Hook index: number, baseDelay?: number Return de useSlideIn Animación stagger en listas
useShimmer Hook - { animatedStyle, shimmerValue } Efecto shimmer para skeletons
usePulse Hook minScale?: number, maxScale?: number { animatedStyle, scale } Animación de pulso
useToggleAnimation Hook isVisible: boolean { animatedStyle } Entrada/salida de elementos
Animated Re-export - - Re-export de reanimated

Configuración por Defecto:

const DEFAULT_TIMING: WithTimingConfig = {
  duration: 300,
  easing: Easing.bezier(0.25, 0.1, 0.25, 1),
};

const DEFAULT_SPRING: WithSpringConfig = {
  damping: 15,
  stiffness: 150,
};

Consumidores:

  • src/app/(tabs)/index.tsx (HomeScreen)
  • src/app/(tabs)/inventory.tsx (InventoryScreen)
  • src/components/ui/Skeleton.tsx

2.1.2 src/hooks/useNetworkStatus.ts

Propósito: Detectar y monitorear estado de conexión de red

Líneas de Código: 73 Dependencias Externas: @react-native-community/netinfo Exports:

Export Tipo Parámetros Retorno Uso
useNetworkStatus Hook - NetworkStatus Estado completo de red
useIsOnline Hook - boolean ¿Está online?
useIsOffline Hook - boolean ¿Está offline?

Interface NetworkStatus:

interface NetworkStatus {
  isConnected: boolean;
  isInternetReachable: boolean | null;
  type: NetInfoStateType;
  isWifi: boolean;
  isCellular: boolean;
}

Comportamiento:

  • Suscripción a eventos de NetInfo en useEffect
  • Fetch inicial del estado de red
  • Cleanup automático en unmount
  • Manejo de isInternetReachable null (estado indeterminado)

Consumidores:

  • src/components/ui/OfflineBanner.tsx

2.1.3 src/theme/ThemeContext.tsx

Propósito: Sistema de temas con soporte light/dark mode

Líneas de Código: 77 Dependencias Externas: react (useColorScheme) Exports:

Export Tipo Descripción
ThemeColors Interface Definición de colores del tema
Theme Interface { colors: ThemeColors, isDark: boolean }
ThemeProvider Component Provider del contexto
useTheme Hook Acceso al tema completo
useColors Hook Acceso solo a colores

Paleta Light:

const lightColors: ThemeColors = {
  primary: '#2563eb',      // Azul principal
  primaryLight: '#f0f9ff', // Azul claro
  background: '#f5f5f5',   // Gris fondo
  card: '#ffffff',         // Blanco tarjetas
  text: '#1a1a1a',         // Negro texto
  textSecondary: '#666666',// Gris texto
  border: '#e5e5e5',       // Gris bordes
  error: '#ef4444',        // Rojo error
  success: '#22c55e',      // Verde éxito
  warning: '#f59e0b',      // Naranja warning
};

Paleta Dark:

const darkColors: ThemeColors = {
  primary: '#3b82f6',
  primaryLight: '#1e3a5f',
  background: '#0f0f0f',
  card: '#1a1a1a',
  text: '#ffffff',
  textSecondary: '#a3a3a3',
  border: '#2d2d2d',
  error: '#f87171',
  success: '#4ade80',
  warning: '#fbbf24',
};

Consumidores:

  • src/app/_layout.tsx (Provider)
  • src/components/ui/Skeleton.tsx
  • src/components/ui/AnimatedList.tsx
  • src/components/skeletons/*.tsx (4 archivos)

2.1.4 src/components/ui/Skeleton.tsx

Propósito: Componentes skeleton base con animación shimmer

Líneas de Código: 216 Dependencias:

  • react-native-reanimated
  • ../../theme/ThemeContext

Exports:

Export Tipo Props Descripción
Skeleton Component width?, height?, borderRadius?, style? Skeleton base
SkeletonText Component width?, height?, style? Línea de texto
SkeletonCircle Component size?, style? Avatar circular
SkeletonImage Component width?, height?, borderRadius?, style? Imagen cuadrada
SkeletonCard Component style? Tarjeta completa
SkeletonListItem Component style? Item de lista
SkeletonStat Component style? Estadística
SkeletonList Component count?, style? Lista de skeletons

Animación Shimmer:

shimmerValue.value = withRepeat(
  withTiming(1, { duration: 1200 }),
  -1,    // Infinito
  false  // Sin reverse
);

// Interpolación de opacidad
opacity: interpolate(shimmerValue.value, [0, 0.5, 1], [0.3, 0.6, 0.3])

Consumidores:

  • src/app/(tabs)/index.tsx (HomeSkeleton)
  • src/components/skeletons/InventoryItemSkeleton.tsx
  • src/components/skeletons/StoreCardSkeleton.tsx
  • src/components/skeletons/CreditCardSkeleton.tsx
  • src/components/skeletons/NotificationSkeleton.tsx

2.1.5 src/components/ui/OfflineBanner.tsx

Propósito: Banner visual que indica pérdida de conexión

Líneas de Código: 115 Dependencias:

  • react-native-reanimated
  • react-native-safe-area-context
  • ../../hooks/useNetworkStatus
  • @expo/vector-icons

Exports:

Export Tipo Props Descripción
OfflineBanner Component message?, showIcon? Banner offline
WithOfflineBanner Component children Wrapper con banner

Comportamiento:

  • Posición absoluta top con z-index 9999
  • Animación slide desde arriba con spring
  • Respeta safe area insets
  • Solo renderiza cuando isOffline === true
  • Color rojo (#EF4444) para indicar problema

Consumidores:

  • src/app/_layout.tsx

2.1.6 src/components/ui/AnimatedList.tsx

Propósito: FlatList con animaciones de entrada staggered

Líneas de Código: 155 Dependencias:

  • react-native-reanimated
  • ../../theme/ThemeContext

Exports:

Export Tipo Descripción
AnimatedList<T> Generic Component FlatList con animaciones
useListItemEntering Hook Crear entering animation
AnimatedListItem Component Item individual animado

Props AnimatedList:

interface AnimatedListProps<T> {
  renderItem: (info: { item: T; index: number }) => ReactElement;
  staggerDelay?: number;      // Default: 50ms
  animationType?: 'fade' | 'slide' | 'spring';  // Default: 'fade'
  animateOnRefresh?: boolean; // Default: true
  onRefresh?: () => Promise<void> | void;
  isRefreshing?: boolean;
  // ...FlatListProps
}

Tipos de Animación:

Tipo Animación
fade FadeIn con delay
slide SlideInRight con delay
spring FadeIn springify

Consumidores:

  • Disponible para uso en cualquier pantalla con listas

2.1.7-10 Skeletons Específicos

Archivos:

  • src/components/skeletons/InventoryItemSkeleton.tsx (72 líneas)
  • src/components/skeletons/StoreCardSkeleton.tsx (68 líneas)
  • src/components/skeletons/CreditCardSkeleton.tsx (115 líneas)
  • src/components/skeletons/NotificationSkeleton.tsx (62 líneas)

Exports por Archivo:

Archivo Exports
InventoryItemSkeleton InventoryItemSkeleton, InventoryListSkeleton, InventoryStatsSkeleton
StoreCardSkeleton StoreCardSkeleton, StoreListSkeleton
CreditCardSkeleton CreditBalanceSkeleton, TransactionSkeleton, TransactionListSkeleton, CreditPackageSkeleton, CreditPackageListSkeleton
NotificationSkeleton NotificationItemSkeleton, NotificationListSkeleton, NotificationHeaderSkeleton

2.2 Archivos Modificados - Análisis Completo


2.2.1 Stores (4 archivos)

Patrón de Modificación Común:

// ANTES
export const useStore = create<State>()((set, get) => ({
  // ...state
}));

// DESPUÉS
import { persist, createJSONStorage } from 'zustand/middleware';
import AsyncStorage from '@react-native-async-storage/async-storage';

export const useStore = create<State>()(
  persist(
    (set, get) => ({
      // ...state
      lastFetched: null,  // NUEVO campo
    }),
    {
      name: 'miinventario-{store-name}',
      storage: createJSONStorage(() => AsyncStorage),
      partialize: (state) => ({
        // Solo datos, no funciones
      }),
    }
  )
);

Detalle por Store:

Store Nombre Persistencia Datos Persistidos Límite
stores.store.ts miinventario-stores stores, currentStore, total, lastFetched Sin límite
inventory.store.ts miinventario-inventory items, total, selectedStoreId, lastFetched 100 items
credits.store.ts miinventario-credits balance, transactions, transactionsTotal, lastFetched 50 tx
notifications.store.ts miinventario-notifications notifications, unreadCount, total, lastFetched 50 notif

Campos Nuevos:

  • lastFetched: number | null - Timestamp de última carga (para futuro stale-while-revalidate)

2.2.2 src/app/_layout.tsx

Cambios Realizados:

# Cambio Tipo Impacto
1 Import OfflineBanner Nuevo import -
2 Import ThemeProvider Nuevo import -
3 Wrap con ThemeProvider Nuevo wrapper Tema disponible globalmente
4 Agregar <OfflineBanner /> Nuevo componente Banner visible globalmente
5 animation: 'slide_from_right' Nueva opción Transiciones mejoradas
6 animationDuration: 250 Nueva opción Duración consistente

Jerarquía de Providers:

GestureHandlerRootView
└── ThemeProvider          ← NUEVO
    └── SafeAreaProvider
        └── QueryClientProvider
            └── View
                ├── OfflineBanner  ← NUEVO
                ├── StatusBar
                └── Stack (con animación) ← MODIFICADO

2.2.3 src/app/(tabs)/index.tsx

Cambios Realizados:

# Cambio Descripción
1 Imports reanimated FadeIn, FadeInDown, FadeInRight, Layout
2 Import hooks useFadeIn, usePressScale
3 Import skeletons Skeleton, SkeletonText, SkeletonStat
4 AnimatedTouchable Animated.createAnimatedComponent(TouchableOpacity)
5 ActionCard component Nuevo componente con animación
6 StatCard component Nuevo componente con animación
7 HomeSkeleton component Skeleton para carga inicial
8 Estado initialLoad Control de skeleton vs contenido
9 Animaciones en header FadeIn.duration(400)
10 Animaciones en cards FadeInDown, FadeInRight con delays

Componentes Nuevos Internos:

function ActionCard({ icon, title, description, onPress, index }) {
  const { animatedStyle, onPressIn, onPressOut } = usePressScale(0.98);
  return (
    <Animated.View entering={FadeInRight.delay(200 + index * 100)}>
      <AnimatedTouchable style={[styles.actionCard, animatedStyle]} ...>
        ...
      </AnimatedTouchable>
    </Animated.View>
  );
}

function StatCard({ value, label, index }) {
  return (
    <Animated.View entering={FadeInDown.delay(400 + index * 100)}>
      ...
    </Animated.View>
  );
}

function HomeSkeleton() {
  return (
    <View>
      <SkeletonText /><Skeleton /><SkeletonStat />...
    </View>
  );
}

2.2.4 src/app/(tabs)/inventory.tsx

Cambios Realizados:

# Cambio Descripción
1 Imports reanimated FadeIn, FadeInDown, FadeInRight, FadeOut, Layout
2 Import hooks usePressScale
3 Import skeletons InventoryListSkeleton
4 AnimatedTouchable Para items de lista
5 InventoryItemCard component Nuevo componente con animación
6 Estado initialLoad Control de skeleton vs lista
7 Animaciones en header FadeIn, FadeInDown
8 Animaciones en items FadeInRight con delay
9 Animación de salida FadeOut al eliminar
10 Layout.springify() Animación de reordenamiento

Componente InventoryItemCard:

function InventoryItemCard({ item, index }) {
  const { animatedStyle, onPressIn, onPressOut } = usePressScale(0.98);

  return (
    <Animated.View
      entering={FadeInRight.delay(Math.min(index * 50, 300)).duration(300)}
      exiting={FadeOut.duration(200)}
      layout={Layout.springify()}
    >
      <AnimatedTouchable style={[styles.itemCard, animatedStyle]} ...>
        {/* Item content */}
      </AnimatedTouchable>
    </Animated.View>
  );
}

2.3 Resumen de Líneas de Código

Categoría Archivos Líneas Nuevas Líneas Modificadas
Hooks 2 260 0
Theme 1 77 0
Components/UI 3 486 0
Components/Skeletons 4 317 0
Stores 4 0 ~80 (20 por archivo)
Screens 3 0 ~200

Total: ~1,140 líneas nuevas + ~280 líneas modificadas


2.4 Validación de Exports/Imports

Grafo de Dependencias Internas:

ThemeContext.tsx
    ↓
┌───┴────────────────────────────────────────┐
│                                            │
Skeleton.tsx ← AnimatedList.tsx              │
    ↓                                        │
┌───┴───────────────────────┐               │
│   │   │   │               │               │
│  Inventory  Store  Credit  Notification   │
│  Skeleton   Skeleton Skeleton Skeleton    │
└───────────────────────────────────────────┘

useNetworkStatus.ts
    ↓
OfflineBanner.tsx
    ↓
_layout.tsx

useAnimations.ts
    ↓
┌───┴───────────────┐
│                   │
(tabs)/index.tsx   (tabs)/inventory.tsx

Validación de Imports:

Archivo Imports ¿Válido?
useAnimations.ts react, react-native-reanimated
useNetworkStatus.ts react, @react-native-community/netinfo
ThemeContext.tsx react, react-native
Skeleton.tsx react, react-native, react-native-reanimated, ThemeContext
OfflineBanner.tsx react, react-native, reanimated, safe-area, useNetworkStatus, icons
AnimatedList.tsx react, react-native, reanimated, ThemeContext
_layout.tsx expo-router, react-query, etc, OfflineBanner, ThemeContext

2.5 Conclusión Fase 2

Estado: Análisis Detallado Completado

Hallazgos:

  1. Todos los archivos nuevos siguen patrones consistentes
  2. No hay dependencias circulares
  3. Todos los imports son válidos
  4. Los stores mantienen compatibilidad hacia atrás
  5. Las pantallas mantienen funcionalidad existente + mejoras

Siguiente Fase: Planeación de documentación basada en este análisis


Última Actualización: 2026-01-12 Responsable: Claude Opus 4.5