Skip to content

Stores (Pinia)

El proyecto usa Pinia con Composition API (setup stores). Hay 7 stores, cada uno responsable de un dominio de estado. Se auto-importan — solo úsalos con useXxxStore().


El store más crítico. Maneja sesión, JWT, y estado de suscripción.

Estado:

  • jwt — Token de sesión (persistido en localStorage)
  • isLoggedIn — Computed derivado de si hay JWT
  • activeSubscriptionsIds — IDs de suscripciones activas en Stripe

Métodos:

  • validateSession() — Valida JWT contra backend, determina estado de suscripción. Usa promise cache para no duplicar calls
  • logout() — Limpia todo el estado, redirige si estaba en página privada

Uso típico:

const authStore = useAuthStore()
if (!authStore.isLoggedIn) {
navigateTo('/iniciar-sesion')
}

Estado:

  • user — Datos completos del usuario (nombre, email, país, suscripciones)
  • isLoading — Cargando datos
  • error — Error de la última petición

Métodos:

  • fetchUser(force?) — Obtiene datos del usuario via API. Cachea en localStorage. Si force=true, refetch obligatorio
  • clearUser() — Limpia datos (usado en logout)

Persistencia: Los datos del usuario se guardan en localStorage bajo key "userData" para evitar un fetch en cada refresh.


Estado:

  • language — Código del idioma actual ('es' | 'en' | 'pt-BR')

Métodos:

  • setLanguage(lang) — Cambia idioma + persiste en localStorage
  • isValidLanguage(code) — Valida si un código es soportado

Comportamiento: Se inicializa leyendo localStorage. Si no hay valor guardado, usa el idioma por defecto del proyecto.


categories.ts — Categorías de contenido

Section titled “categories.ts — Categorías de contenido”

Estado:

  • categories — Array de categorías obtenidas del CMS
  • isPending — Cargando

Métodos:

  • fetchCategories(force?) — Obtiene categorías. Cachea con flag isLoaded

Comportamiento especial: Tiene un watcher sobre el idioma — cuando el usuario cambia de idioma, las categorías se refetchean automáticamente para obtener los nombres traducidos.


orders.ts — Productos comprados y suscripción

Section titled “orders.ts — Productos comprados y suscripción”

Estado:

  • userContents — Productos comprados por el usuario
  • osteocomContents — Productos Osteocom comprados
  • isUserSubscribed — Si tiene suscripción activa
  • userSubscriptionData — Detalles de la suscripción (fechas, estado de cancelación)
  • isManuallyCancelledSubscription — Si canceló manualmente

Métodos:

  • fetchUserSubscriptionConfirmation() — Verifica suscripción contra backend
  • fetchUserProducts(force?) — Obtiene historial de pedidos
  • checkIsProductPurchased(SKU) — Verifica si un producto específico fue comprado
  • checkIsOsteocomProductPurchased(idChannel) — Lo mismo para Osteocom
  • clearUserProducts() — Reset completo (usado en logout)

Uso típico:

const ordersStore = useOrdersStore()
// Verificar si el usuario ya compró algo
const hasCourse = ordersStore.checkIsProductPurchased('COURSE-001')
// Verificar suscripción
if (ordersStore.isUserSubscribed) {
// acceso al contenido incluido en la suscripción
}

Maneja la persistencia de configuración de reproducción de video.

Estado persistido (localStorage):

  • volume — Volumen del reproductor
  • muted — Si está muteado
  • speed — Velocidad de reproducción (1x, 1.5x, 2x)
  • quality — Calidad seleccionada (para Plyr)
  • times — Record { [videoId]: lastPosition } — guarda posición por video

Comportamiento:

  • Al abrir un video, aplica automáticamente la config guardada (volumen, velocidad)
  • Guarda posición de reproducción cada 5 segundos (throttled)
  • Al reabrir un video, resume desde donde se dejó
  • Cuando un video termina, limpia la posición guardada
  • Usa WeakSet/WeakMap para tracking de elementos DOM sin memory leaks

addressSync.ts — Dirección de facturación

Section titled “addressSync.ts — Dirección de facturación”

Sincroniza la dirección de facturación del usuario entre formularios y el backend. Store auxiliar usado en el flujo de checkout.


Un store no debe importar otro store directamente (riesgo de dependencia circular). Si necesitas datos de otro store, obtenerlos desde el componente:

// En componente — correcto
const auth = useAuthStore()
const orders = useOrdersStore()
// En store — evitar
// import { useAuthStore } from './auth' // No hacer esto

Los stores que persisten datos usan un patrón consistente:

// Leer al inicializar
const data = ref(JSON.parse(localStorage.getItem('key') || 'null'))
// Persistir al cambiar
watch(data, (val) => {
localStorage.setItem('key', JSON.stringify(val))
})
// Limpiar en logout
localStorage.removeItem('key')

Todos los stores incluyen soporte para Hot Module Replacement:

if (import.meta.hot) {
import.meta.hot.accept(acceptHMRUpdate(useMyStore, import.meta.hot))
}

Esto evita que el estado se pierda al editar el store durante desarrollo.