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().
auth.ts — Autenticación
Section titled “auth.ts — Autenticación”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 JWTactiveSubscriptionsIds— IDs de suscripciones activas en Stripe
Métodos:
validateSession()— Valida JWT contra backend, determina estado de suscripción. Usa promise cache para no duplicar callslogout()— Limpia todo el estado, redirige si estaba en página privada
Uso típico:
const authStore = useAuthStore()
if (!authStore.isLoggedIn) { navigateTo('/iniciar-sesion')}user.ts — Perfil de usuario
Section titled “user.ts — Perfil de usuario”Estado:
user— Datos completos del usuario (nombre, email, país, suscripciones)isLoading— Cargando datoserror— Error de la última petición
Métodos:
fetchUser(force?)— Obtiene datos del usuario via API. Cachea en localStorage. Siforce=true, refetch obligatorioclearUser()— Limpia datos (usado en logout)
Persistencia: Los datos del usuario se guardan en localStorage bajo key "userData" para evitar un fetch en cada refresh.
language.ts — Idioma
Section titled “language.ts — Idioma”Estado:
language— Código del idioma actual ('es'|'en'|'pt-BR')
Métodos:
setLanguage(lang)— Cambia idioma + persiste en localStorageisValidLanguage(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 CMSisPending— Cargando
Métodos:
fetchCategories(force?)— Obtiene categorías. Cachea con flagisLoaded
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 usuarioosteocomContents— Productos Osteocom compradosisUserSubscribed— Si tiene suscripción activauserSubscriptionData— Detalles de la suscripción (fechas, estado de cancelación)isManuallyCancelledSubscription— Si canceló manualmente
Métodos:
fetchUserSubscriptionConfirmation()— Verifica suscripción contra backendfetchUserProducts(force?)— Obtiene historial de pedidoscheckIsProductPurchased(SKU)— Verifica si un producto específico fue compradocheckIsOsteocomProductPurchased(idChannel)— Lo mismo para OsteocomclearUserProducts()— Reset completo (usado en logout)
Uso típico:
const ordersStore = useOrdersStore()
// Verificar si el usuario ya compró algoconst hasCourse = ordersStore.checkIsProductPurchased('COURSE-001')
// Verificar suscripciónif (ordersStore.isUserSubscribed) { // acceso al contenido incluido en la suscripción}video.ts — Reproducción de video
Section titled “video.ts — Reproducción de video”Maneja la persistencia de configuración de reproducción de video.
Estado persistido (localStorage):
volume— Volumen del reproductormuted— Si está muteadospeed— 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/WeakMappara 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.
Patrones importantes
Section titled “Patrones importantes”No acceder stores cruzados
Section titled “No acceder stores cruzados”Un store no debe importar otro store directamente (riesgo de dependencia circular). Si necesitas datos de otro store, obtenerlos desde el componente:
// En componente — correctoconst auth = useAuthStore()const orders = useOrdersStore()
// En store — evitar// import { useAuthStore } from './auth' // No hacer estoPersistencia en localStorage
Section titled “Persistencia en localStorage”Los stores que persisten datos usan un patrón consistente:
// Leer al inicializarconst data = ref(JSON.parse(localStorage.getItem('key') || 'null'))
// Persistir al cambiarwatch(data, (val) => { localStorage.setItem('key', JSON.stringify(val))})
// Limpiar en logoutlocalStorage.removeItem('key')HMR support
Section titled “HMR support”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.