794 lines
20 KiB
Markdown
794 lines
20 KiB
Markdown
# 📚 Sistema de Stores - Analítica Núcleo
|
||
|
||
Este directorio contiene el sistema de gestión de estado de la aplicación usando **Pinia** y **localStorage** para caché persistente.
|
||
|
||
## 📋 Índice
|
||
|
||
- [Arquitectura General](#-arquitectura-general)
|
||
- [Stores Disponibles](#-stores-disponibles)
|
||
- [Metadata Store](#-metadata-store)
|
||
- [Table Data Factory](#-table-data-factory)
|
||
- [Flujo de Datos](#-flujo-de-datos)
|
||
- [Uso en Componentes](#-uso-en-componentes)
|
||
- [API Reference](#-api-reference)
|
||
- [Cache y Persistencia](#-cache-y-persistencia)
|
||
- [Ejemplos Avanzados](#-ejemplos-avanzados)
|
||
|
||
---
|
||
|
||
## 🏗️ Arquitectura General
|
||
|
||
El sistema de stores se compone de dos partes principales:
|
||
|
||
```
|
||
app/stores/
|
||
├── metadata.ts # Store de metadatos de tablas
|
||
├── tableDataFactory.ts # Factory para crear stores de datos
|
||
└── README.md # Este archivo
|
||
```
|
||
|
||
### Flujo de Datos
|
||
|
||
```mermaid
|
||
graph TB
|
||
A[App Startup] --> B[Metadata Store]
|
||
B --> C{¿Tiene cache?}
|
||
C -->|Sí| D[Carga desde localStorage]
|
||
C -->|No| E[Fetch desde API]
|
||
E --> F[/api/metadata]
|
||
F --> G[Guarda en localStorage]
|
||
D --> H[Metadata disponible]
|
||
G --> H
|
||
|
||
H --> I[Plugin Auto-registro]
|
||
I --> J[Crea stores por tabla]
|
||
|
||
K[Usuario selecciona tabla] --> L{¿Store existe?}
|
||
L -->|Sí| M{¿Tiene cache?}
|
||
L -->|No| N[Crea store]
|
||
N --> M
|
||
M -->|Sí| O[Carga desde cache]
|
||
M -->|No| P[Fetch desde API]
|
||
P --> Q[/api/data/:table]
|
||
Q --> R[Guarda en cache]
|
||
O --> S[Datos disponibles]
|
||
R --> S
|
||
```
|
||
|
||
---
|
||
|
||
## 🗂️ Stores Disponibles
|
||
|
||
### 1. **Metadata Store** (`metadata.ts`)
|
||
|
||
Store global que gestiona la información de todas las tablas disponibles.
|
||
|
||
**Responsabilidades:**
|
||
- Mantener lista de tablas disponibles
|
||
- Información estructural (columnas, claves primarias, etc.)
|
||
- Estadísticas de cada tabla
|
||
- Cache persistente de metadatos
|
||
|
||
### 2. **Table Data Stores** (Factory pattern)
|
||
|
||
Stores dinámicos, uno por cada tabla de la base de datos.
|
||
|
||
**Responsabilidades:**
|
||
- Almacenar registros de una tabla específica
|
||
- Gestionar estado de carga
|
||
- Cache persistente por tabla
|
||
- Operaciones de filtrado y búsqueda
|
||
|
||
---
|
||
|
||
## 🔍 Metadata Store
|
||
|
||
### Estructura de Datos
|
||
|
||
```typescript
|
||
interface TableMetadata {
|
||
table: string // Nombre de la tabla
|
||
rowCount: number // Total de registros
|
||
primaryKey: string // Clave primaria
|
||
approxSizeBytes: number // Tamaño aproximado en bytes
|
||
columns: string[] // Lista de columnas
|
||
createdAtRange?: {
|
||
from: string // Fecha de creación más antigua
|
||
to: string // Fecha de creación más reciente
|
||
}
|
||
lastRefreshed?: string // Última actualización de metadatos
|
||
sampleRow?: Record<string, unknown> // Registro de ejemplo
|
||
}
|
||
```
|
||
|
||
### Estado del Store
|
||
|
||
```typescript
|
||
{
|
||
metadata: TableMetadata[] // Array de metadatos por tabla
|
||
loading: boolean // Estado de carga
|
||
error: string | null // Mensaje de error
|
||
lastUpdated: string | null // Timestamp última actualización
|
||
initialized: boolean // Si el store está inicializado
|
||
}
|
||
```
|
||
|
||
### Getters Disponibles
|
||
|
||
| Getter | Tipo | Descripción |
|
||
|--------|------|-------------|
|
||
| `allTables` | `TableMetadata[]` | Todas las tablas disponibles |
|
||
| `getTableMetadata(name)` | `TableMetadata \| undefined` | Metadatos de una tabla específica |
|
||
| `totalTables` | `number` | Cantidad total de tablas |
|
||
| `totalRecords` | `number` | Suma de registros de todas las tablas |
|
||
| `tableNames` | `string[]` | Lista de nombres de tablas |
|
||
| `hasMetadata` | `boolean` | Si hay metadatos cargados |
|
||
| `isLoading` | `boolean` | Si está cargando actualmente |
|
||
| `hasError` | `boolean` | Si hay un error |
|
||
| `formattedLastUpdated` | `string` | Fecha formateada de última actualización |
|
||
| `isStale` | `boolean` | Si los datos tienen > 5 minutos |
|
||
|
||
### Actions Disponibles
|
||
|
||
| Action | Parámetros | Descripción |
|
||
|--------|------------|-------------|
|
||
| `loadMetadata(force?)` | `force: boolean` | Carga lazy, solo si necesario |
|
||
| `refreshMetadata()` | - | Actualización forzada |
|
||
| `clearMetadata()` | - | Limpia cache y datos |
|
||
| `initialize()` | - | Inicializa el store (cache + fetch) |
|
||
|
||
### Fuente de Datos
|
||
|
||
**Endpoint:** `GET /api/metadata`
|
||
|
||
**Respuesta:**
|
||
```json
|
||
[
|
||
{
|
||
"table": "usuarios",
|
||
"rowCount": 1523,
|
||
"primaryKey": "id",
|
||
"approxSizeBytes": 245760,
|
||
"columns": ["id", "nombre", "email", "created_at"],
|
||
"createdAtRange": {
|
||
"from": "2024-01-01T00:00:00Z",
|
||
"to": "2025-01-20T15:30:00Z"
|
||
}
|
||
},
|
||
// ... más tablas
|
||
]
|
||
```
|
||
|
||
### Uso Básico
|
||
|
||
```typescript
|
||
import { useMetadataStore } from '~/stores/metadata'
|
||
|
||
// En un componente
|
||
const metadataStore = useMetadataStore()
|
||
|
||
// Inicializar (carga desde cache o API)
|
||
await metadataStore.initialize()
|
||
|
||
// Acceder a los datos
|
||
console.log(metadataStore.allTables)
|
||
console.log(metadataStore.totalTables)
|
||
|
||
// Obtener metadatos de una tabla específica
|
||
const usuariosMetadata = metadataStore.getTableMetadata('usuarios')
|
||
|
||
// Refrescar metadatos
|
||
await metadataStore.refreshMetadata()
|
||
|
||
// Verificar frescura de datos
|
||
if (metadataStore.isStale) {
|
||
console.log('Los metadatos tienen más de 5 minutos')
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 🏭 Table Data Factory
|
||
|
||
Sistema factory que crea stores dinámicos para cada tabla.
|
||
|
||
### Estructura de Datos
|
||
|
||
```typescript
|
||
interface TableDataState<T = Record<string, unknown>> {
|
||
data: T[] // Registros cacheados
|
||
loading: boolean // Estado de carga
|
||
error: string | null // Mensaje de error
|
||
lastUpdated: string | null // Timestamp última actualización
|
||
initialized: boolean // Si el store está inicializado
|
||
limit: number // Límite de registros
|
||
}
|
||
```
|
||
|
||
### Getters Disponibles
|
||
|
||
| Getter | Tipo | Descripción |
|
||
|--------|------|-------------|
|
||
| `allRecords` | `T[]` | Todos los registros cacheados |
|
||
| `hasData` | `boolean` | Si hay datos disponibles |
|
||
| `isLoading` | `boolean` | Si está cargando actualmente |
|
||
| `hasError` | `boolean` | Si hay un error |
|
||
| `recordCount` | `number` | Cantidad de registros |
|
||
| `formattedLastUpdated` | `string` | Fecha formateada |
|
||
| `isStale` | `boolean` | Si los datos tienen > 5 minutos |
|
||
|
||
### Actions Disponibles
|
||
|
||
| Action | Parámetros | Descripción |
|
||
|--------|------------|-------------|
|
||
| `loadData(force?)` | `force: boolean` | Carga lazy |
|
||
| `refreshData()` | - | Actualización forzada |
|
||
| `clearData()` | - | Limpia cache y datos |
|
||
| `initialize()` | - | Inicializa (cache + fetch) |
|
||
| `getRecord(id)` | `id: string \| number` | Obtiene un registro por ID |
|
||
| `filterRecords(predicate)` | `predicate: (record: T) => boolean` | Filtra registros |
|
||
|
||
### Fuente de Datos
|
||
|
||
**Endpoint:** `GET /api/data/:tableName?limit=100`
|
||
|
||
**Respuesta:**
|
||
```json
|
||
{
|
||
"table": "usuarios",
|
||
"count": 100,
|
||
"limit": 100,
|
||
"records": [
|
||
{
|
||
"id": 1,
|
||
"nombre": "Juan Pérez",
|
||
"email": "juan@example.com",
|
||
"created_at": "2024-01-15T10:30:00Z"
|
||
},
|
||
// ... más registros
|
||
]
|
||
}
|
||
```
|
||
|
||
### Creación de Stores
|
||
|
||
#### Método 1: Usando la Factory Directamente
|
||
|
||
```typescript
|
||
import { createTableDataStore } from '~/stores/tableDataFactory'
|
||
|
||
// Crear un store para la tabla "usuarios"
|
||
const useUsuariosStore = createTableDataStore('usuarios', 100)
|
||
|
||
// Usar el store
|
||
const usuariosStore = useUsuariosStore()
|
||
await usuariosStore.initialize()
|
||
|
||
console.log(usuariosStore.allRecords)
|
||
```
|
||
|
||
#### Método 2: Usando el Helper
|
||
|
||
```typescript
|
||
import { useTableDataStore } from '~/stores/tableDataFactory'
|
||
|
||
// Crear y usar el store en un solo paso
|
||
const usuariosStore = useTableDataStore('usuarios', 100)
|
||
await usuariosStore.initialize()
|
||
```
|
||
|
||
#### Método 3: Usando el Plugin (Recomendado)
|
||
|
||
```typescript
|
||
// El plugin auto-registra todos los stores
|
||
const { $getTableStore } = useNuxtApp()
|
||
|
||
// Obtener un store ya registrado
|
||
const usuariosStore = $getTableStore('usuarios')
|
||
|
||
if (usuariosStore) {
|
||
await usuariosStore.initialize()
|
||
console.log(usuariosStore.allRecords)
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 🔄 Flujo de Datos
|
||
|
||
### Inicialización de la App
|
||
|
||
```typescript
|
||
// 1. App.vue o plugin se ejecuta
|
||
onMounted(async () => {
|
||
// 2. Metadata store se inicializa
|
||
const metadataStore = useMetadataStore()
|
||
await metadataStore.initialize()
|
||
|
||
// 3. Plugin auto-registra stores (tableStores.client.ts)
|
||
// Se crean stores para cada tabla encontrada en metadataStore.allTables
|
||
|
||
// 4. Los stores están listos pero NO han cargado datos aún
|
||
// Esto es lazy loading - solo cargan cuando se necesitan
|
||
})
|
||
```
|
||
|
||
### Carga de Datos de una Tabla
|
||
|
||
```typescript
|
||
// Usuario selecciona tabla "usuarios"
|
||
const selectTable = async (tableName: string) => {
|
||
// 1. Obtener o crear store
|
||
const store = $getTableStore(tableName) || useTableDataStore(tableName)
|
||
|
||
// 2. Inicializar (intenta cache primero)
|
||
await store.initialize()
|
||
|
||
// Flujo interno de initialize():
|
||
// ├─ loadFromCache()
|
||
// │ └─ Lee localStorage['table-data-usuarios']
|
||
// │ ├─ Si existe → usa datos cacheados
|
||
// │ └─ Si no existe → continúa
|
||
// └─ loadData()
|
||
// └─ fetchData()
|
||
// └─ $fetch('/api/data/usuarios?limit=100')
|
||
// └─ Guarda en localStorage
|
||
// └─ Actualiza store
|
||
|
||
// 3. Datos disponibles
|
||
console.log(store.allRecords)
|
||
}
|
||
```
|
||
|
||
### Actualización Manual
|
||
|
||
```typescript
|
||
// Usuario hace click en "Actualizar datos"
|
||
const refreshTable = async () => {
|
||
// 1. Ejecutar refreshData()
|
||
await store.refreshData()
|
||
|
||
// Flujo interno de refreshData():
|
||
// └─ fetchData()
|
||
// ├─ loading = true
|
||
// ├─ $fetch('/api/data/usuarios?limit=100')
|
||
// ├─ Actualiza store.data
|
||
// ├─ Actualiza lastUpdated
|
||
// ├─ Guarda en localStorage
|
||
// └─ loading = false
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 💻 Uso en Componentes
|
||
|
||
### Ejemplo Completo: Explorador de Tablas
|
||
|
||
```vue
|
||
<template>
|
||
<div>
|
||
<!-- Selector de tabla -->
|
||
<select v-model="selectedTable" @change="onTableChange">
|
||
<option v-for="table in metadataStore.tableNames" :key="table">
|
||
{{ table }}
|
||
</option>
|
||
</select>
|
||
|
||
<!-- Información de cache -->
|
||
<div v-if="currentStore">
|
||
<p>Última actualización: {{ currentStore.formattedLastUpdated }}</p>
|
||
<p v-if="currentStore.isStale" class="warning">
|
||
⚠️ Datos desactualizados
|
||
</p>
|
||
<button @click="refreshData" :disabled="currentStore.isLoading">
|
||
{{ currentStore.isLoading ? 'Actualizando...' : 'Actualizar' }}
|
||
</button>
|
||
</div>
|
||
|
||
<!-- Tabla de datos -->
|
||
<div v-if="currentStore?.hasData">
|
||
<p>{{ currentStore.recordCount }} registros</p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th v-for="col in columns" :key="col">{{ col }}</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr v-for="record in currentStore.allRecords" :key="record.id">
|
||
<td v-for="col in columns" :key="col">
|
||
{{ record[col] }}
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
|
||
<!-- Estados -->
|
||
<div v-else-if="currentStore?.isLoading">Cargando...</div>
|
||
<div v-else-if="currentStore?.hasError">
|
||
Error: {{ currentStore.error }}
|
||
</div>
|
||
<div v-else>Selecciona una tabla</div>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
import { useMetadataStore } from '~/stores/metadata'
|
||
import { useTableDataStore } from '~/stores/tableDataFactory'
|
||
|
||
const metadataStore = useMetadataStore()
|
||
const selectedTable = ref('')
|
||
const currentStore = ref<ReturnType<typeof useTableDataStore> | null>(null)
|
||
|
||
// Columnas de la tabla actual
|
||
const columns = computed(() => {
|
||
if (!currentStore.value?.hasData) return []
|
||
const firstRecord = currentStore.value.allRecords[0]
|
||
return Object.keys(firstRecord)
|
||
})
|
||
|
||
// Cambio de tabla
|
||
async function onTableChange() {
|
||
if (!selectedTable.value) return
|
||
|
||
// Obtener store de la tabla
|
||
currentStore.value = useTableDataStore(selectedTable.value)
|
||
|
||
// Inicializar (usa cache si existe)
|
||
await currentStore.value.initialize()
|
||
}
|
||
|
||
// Refrescar datos
|
||
async function refreshData() {
|
||
if (!currentStore.value) return
|
||
await currentStore.value.refreshData()
|
||
}
|
||
|
||
// Inicializar metadatos al montar
|
||
onMounted(async () => {
|
||
await metadataStore.initialize()
|
||
|
||
// Auto-seleccionar primera tabla
|
||
if (metadataStore.tableNames.length > 0) {
|
||
selectedTable.value = metadataStore.tableNames[0]
|
||
await onTableChange()
|
||
}
|
||
})
|
||
</script>
|
||
```
|
||
|
||
### Ejemplo: Búsqueda y Filtrado
|
||
|
||
```typescript
|
||
import { useTableDataStore } from '~/stores/tableDataFactory'
|
||
|
||
const usuariosStore = useTableDataStore('usuarios')
|
||
await usuariosStore.initialize()
|
||
|
||
// Filtrar usuarios activos
|
||
const usuariosActivos = usuariosStore.filterRecords(
|
||
user => user.activo === true
|
||
)
|
||
|
||
// Buscar usuario por ID
|
||
const usuario = usuariosStore.getRecord(123)
|
||
|
||
// Búsqueda personalizada
|
||
const usuariosPorEmail = usuariosStore.filterRecords(
|
||
user => user.email.includes('@gmail.com')
|
||
)
|
||
|
||
console.log(`Encontrados ${usuariosPorEmail.length} usuarios con Gmail`)
|
||
```
|
||
|
||
---
|
||
|
||
## 📖 API Reference
|
||
|
||
### Metadata Store API
|
||
|
||
```typescript
|
||
interface MetadataStore {
|
||
// State
|
||
metadata: TableMetadata[]
|
||
loading: boolean
|
||
error: string | null
|
||
lastUpdated: string | null
|
||
initialized: boolean
|
||
|
||
// Getters
|
||
allTables: TableMetadata[]
|
||
getTableMetadata(name: string): TableMetadata | undefined
|
||
totalTables: number
|
||
totalRecords: number
|
||
tableNames: string[]
|
||
hasMetadata: boolean
|
||
isLoading: boolean
|
||
hasError: boolean
|
||
formattedLastUpdated: string
|
||
isStale: boolean
|
||
|
||
// Actions
|
||
loadMetadata(force?: boolean): Promise<void>
|
||
refreshMetadata(): Promise<void>
|
||
clearMetadata(): void
|
||
initialize(): Promise<void>
|
||
}
|
||
```
|
||
|
||
### Table Data Store API
|
||
|
||
```typescript
|
||
interface TableDataStore<T> {
|
||
// State
|
||
data: T[]
|
||
loading: boolean
|
||
error: string | null
|
||
lastUpdated: string | null
|
||
initialized: boolean
|
||
limit: number
|
||
|
||
// Getters
|
||
allRecords: T[]
|
||
hasData: boolean
|
||
isLoading: boolean
|
||
hasError: boolean
|
||
recordCount: number
|
||
formattedLastUpdated: string
|
||
isStale: boolean
|
||
|
||
// Actions
|
||
loadData(force?: boolean): Promise<void>
|
||
refreshData(): Promise<void>
|
||
clearData(): void
|
||
initialize(): Promise<void>
|
||
getRecord(id: string | number): T | undefined
|
||
filterRecords(predicate: (record: T) => boolean): T[]
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 💾 Cache y Persistencia
|
||
|
||
### Estrategia de Cache
|
||
|
||
El sistema usa **localStorage** para persistir datos:
|
||
|
||
```typescript
|
||
// Estructura de cache en localStorage
|
||
{
|
||
// Metadatos
|
||
"metadata-cache": {
|
||
metadata: TableMetadata[],
|
||
lastUpdated: "2025-01-20T10:30:00Z"
|
||
},
|
||
|
||
// Datos de tabla usuarios
|
||
"table-data-usuarios": {
|
||
data: [...registros],
|
||
lastUpdated: "2025-01-20T10:35:00Z",
|
||
limit: 100
|
||
},
|
||
|
||
// Datos de tabla productos
|
||
"table-data-productos": {
|
||
data: [...registros],
|
||
lastUpdated: "2025-01-20T10:40:00Z",
|
||
limit: 100
|
||
}
|
||
}
|
||
```
|
||
|
||
### Políticas de Cache
|
||
|
||
1. **Freshness Check**: Los datos se consideran "stale" después de 5 minutos
|
||
2. **Lazy Loading**: Los stores solo cargan cuando se accede a ellos
|
||
3. **Cache-First**: Siempre intenta cargar desde cache primero
|
||
4. **Auto-Refresh**: El usuario controla manualmente cuándo refrescar
|
||
5. **Offline Support**: Datos disponibles incluso sin conexión
|
||
|
||
### Limpieza de Cache
|
||
|
||
```typescript
|
||
// Limpiar cache de metadatos
|
||
const metadataStore = useMetadataStore()
|
||
metadataStore.clearMetadata()
|
||
|
||
// Limpiar cache de una tabla
|
||
const usuariosStore = useTableDataStore('usuarios')
|
||
usuariosStore.clearData()
|
||
|
||
// Limpiar TODO el cache (localStorage)
|
||
if (process.client) {
|
||
localStorage.clear()
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 🎯 Ejemplos Avanzados
|
||
|
||
### Sincronización Automática
|
||
|
||
```typescript
|
||
// Auto-refrescar cada 5 minutos si los datos están stale
|
||
const autoRefresh = () => {
|
||
const store = useTableDataStore('usuarios')
|
||
|
||
setInterval(async () => {
|
||
if (store.isStale && !store.isLoading) {
|
||
console.log('Refrescando datos automáticamente...')
|
||
await store.refreshData()
|
||
}
|
||
}, 5 * 60 * 1000) // 5 minutos
|
||
}
|
||
```
|
||
|
||
### Validación de Datos
|
||
|
||
```typescript
|
||
// Verificar integridad de datos cacheados
|
||
const validateCache = async (tableName: string) => {
|
||
const store = useTableDataStore(tableName)
|
||
const metadata = metadataStore.getTableMetadata(tableName)
|
||
|
||
if (!metadata) return false
|
||
|
||
// Verificar que el número de registros sea razonable
|
||
if (store.recordCount > metadata.rowCount) {
|
||
console.warn('Cache corrupto, limpiando...')
|
||
store.clearData()
|
||
await store.refreshData()
|
||
return false
|
||
}
|
||
|
||
return true
|
||
}
|
||
```
|
||
|
||
### Composable Personalizado
|
||
|
||
```typescript
|
||
// composables/useTableData.ts
|
||
export function useTableData(tableName: string) {
|
||
const store = useTableDataStore(tableName)
|
||
const initialized = ref(false)
|
||
|
||
onMounted(async () => {
|
||
await store.initialize()
|
||
initialized.value = true
|
||
})
|
||
|
||
// Auto-refrescar si stale
|
||
watch(() => store.isStale, async (isStale) => {
|
||
if (isStale && initialized.value) {
|
||
await store.refreshData()
|
||
}
|
||
})
|
||
|
||
return {
|
||
data: computed(() => store.allRecords),
|
||
loading: computed(() => store.isLoading),
|
||
error: computed(() => store.error),
|
||
refresh: () => store.refreshData()
|
||
}
|
||
}
|
||
```
|
||
|
||
### TypeScript Tipado
|
||
|
||
```typescript
|
||
// Definir tipos para tus tablas
|
||
interface Usuario {
|
||
id: number
|
||
nombre: string
|
||
email: string
|
||
activo: boolean
|
||
created_at: string
|
||
}
|
||
|
||
interface Producto {
|
||
id: number
|
||
nombre: string
|
||
precio: number
|
||
stock: number
|
||
}
|
||
|
||
// Usar stores tipados
|
||
const usuariosStore = useTableDataStore<Usuario>('usuarios')
|
||
const productosStore = useTableDataStore<Producto>('productos')
|
||
|
||
// TypeScript ahora conoce la estructura
|
||
usuariosStore.allRecords.forEach(user => {
|
||
console.log(user.email) // ✅ TypeScript sabe que existe
|
||
// console.log(user.foo) // ❌ Error: Property 'foo' does not exist
|
||
})
|
||
```
|
||
|
||
---
|
||
|
||
## 🐛 Debugging
|
||
|
||
### Inspeccionar Estado
|
||
|
||
```typescript
|
||
// En DevTools Console
|
||
import { useMetadataStore } from '~/stores/metadata'
|
||
import { useTableDataStore } from '~/stores/tableDataFactory'
|
||
|
||
const metadataStore = useMetadataStore()
|
||
const usuariosStore = useTableDataStore('usuarios')
|
||
|
||
// Ver estado completo
|
||
console.log('Metadata:', metadataStore.$state)
|
||
console.log('Usuarios:', usuariosStore.$state)
|
||
|
||
// Ver cache
|
||
console.log('Cache:', {
|
||
metadata: localStorage.getItem('metadata-cache'),
|
||
usuarios: localStorage.getItem('table-data-usuarios')
|
||
})
|
||
```
|
||
|
||
### Logs Detallados
|
||
|
||
Descomenta los `console.log` en los stores para ver el flujo completo:
|
||
|
||
```typescript
|
||
// En tableDataFactory.ts línea 144
|
||
console.log(`[${tableName}] Fetching data...`)
|
||
|
||
// En tableDataFactory.ts línea 158
|
||
console.log(`[${tableName}] Data fetched: ${this.recordCount} records`)
|
||
|
||
// En tableDataFactory.ts línea 164
|
||
console.log(`[${tableName}] Saved to cache`)
|
||
```
|
||
|
||
---
|
||
|
||
## 📝 Notas Importantes
|
||
|
||
### ⚠️ Limitaciones
|
||
|
||
1. **Límite de localStorage**: ~5-10MB por dominio
|
||
- Solución: Implementar estrategia LRU si se excede
|
||
|
||
2. **Límite de registros**: Por defecto 100 registros por tabla
|
||
- Modificable en la factory: `useTableDataStore('tabla', 500)`
|
||
|
||
3. **Sin paginación**: Todos los registros se cargan de una vez
|
||
- Para tablas grandes, considerar implementar paginación
|
||
|
||
4. **Sin sincronización real-time**: Los datos se actualizan manualmente
|
||
- Para real-time, considerar WebSockets
|
||
|
||
### 🚀 Performance Tips
|
||
|
||
1. **Lazy Loading**: Los stores no cargan datos hasta que se necesitan
|
||
2. **Cache-First**: Usa cache para respuesta instantánea
|
||
3. **Selectores Computados**: Usa `computed()` para filtros complejos
|
||
4. **Debounce**: Para búsquedas, usa debounce para evitar renders excesivos
|
||
|
||
### 🔒 Seguridad
|
||
|
||
- Los datos en localStorage NO están encriptados
|
||
- No almacenes información sensible (contraseñas, tokens)
|
||
- El API backend debe validar permisos y autenticación
|
||
|
||
---
|
||
|
||
## 📚 Referencias
|
||
|
||
- [Pinia Documentation](https://pinia.vuejs.org/)
|
||
- [Nuxt 4 Documentation](https://nuxt.com/)
|
||
- [LocalStorage API](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage)
|
||
|
||
---
|
||
|
||
**Última actualización:** 2025-01-20
|
||
**Versión:** 1.0.0
|
||
**Autor:** Equipo Núcleo |