537 lines
16 KiB
Markdown
537 lines
16 KiB
Markdown
# 📚 Sistema de Gestión de Contenido - Documentación Completa
|
|
|
|
**Versión:** 1.0.0
|
|
**Fecha:** 2025-01-06
|
|
**Estado:** ✅ Completado
|
|
|
|
---
|
|
|
|
## 📋 Tabla de Contenidos
|
|
|
|
1. [Resumen Ejecutivo](#resumen-ejecutivo)
|
|
2. [Arquitectura del Sistema](#arquitectura-del-sistema)
|
|
3. [Componentes Principales](#componentes-principales)
|
|
4. [Flujo de Datos](#flujo-de-datos)
|
|
5. [API y Endpoints](#api-y-endpoints)
|
|
6. [Content Pack](#content-pack)
|
|
7. [ContentAdapter](#contentadapter)
|
|
8. [Enlaces Bidireccionales](#enlaces-bidireccionales)
|
|
9. [Sistema de Cache](#sistema-de-cache)
|
|
10. [Testing](#testing)
|
|
11. [Guía de Uso](#guía-de-uso)
|
|
12. [Troubleshooting](#troubleshooting)
|
|
|
|
---
|
|
|
|
## 🎯 Resumen Ejecutivo
|
|
|
|
El Sistema de Gestión de Contenido es una arquitectura **aditiva y desacoplada** que permite gestionar todo el contenido de la aplicación PWA sin tocar el código existente. El sistema garantiza **estabilidad total** incluso si el sistema externo falla, mediante un mecanismo de fallback robusto.
|
|
|
|
### Características Principales
|
|
|
|
- ✅ **Arquitectura Aditiva:** No modifica código existente
|
|
- ✅ **Fallback Total:** Funciona offline con datos locales
|
|
- ✅ **Content Pack JSON:** Sistema de distribución de contenido
|
|
- ✅ **Enlaces Bidireccionales:** Protocolos ↔ Guías ↔ Manual
|
|
- ✅ **Cache Inteligente:** IndexedDB con fallback a localStorage
|
|
- ✅ **Testing Completo:** Herramientas de verificación integradas
|
|
|
|
---
|
|
|
|
## 🏗️ Arquitectura del Sistema
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────────────┐
|
|
│ FRONTEND (PWA) │
|
|
├─────────────────────────────────────────────────────────────┤
|
|
│ │
|
|
│ ┌──────────────────────────────────────────────────────┐ │
|
|
│ │ ContentAdapterFactory │ │
|
|
│ │ ┌──────────────────┐ ┌──────────────────┐ │ │
|
|
│ │ │ LocalContentAdapter│ │ExternalContentAdapter│ │ │
|
|
│ │ │ (Datos locales) │ │ (Content Pack) │ │ │
|
|
│ │ └──────────────────┘ └──────────────────┘ │ │
|
|
│ └──────────────────────────────────────────────────────┘ │
|
|
│ │ │
|
|
│ ▼ │
|
|
│ ┌──────────────────────────────────────────────────────┐ │
|
|
│ │ Páginas de la App │ │
|
|
│ │ (RCP, ViaAerea, Shock, Farmacos, etc.) │ │
|
|
│ └──────────────────────────────────────────────────────┘ │
|
|
│ │
|
|
│ ┌──────────────────────────────────────────────────────┐ │
|
|
│ │ Cache (IndexedDB / localStorage) │ │
|
|
│ └──────────────────────────────────────────────────────┘ │
|
|
└─────────────────────────────────────────────────────────────┘
|
|
│
|
|
│ HTTP
|
|
▼
|
|
┌─────────────────────────────────────────────────────────────┐
|
|
│ BACKEND (Express) │
|
|
├─────────────────────────────────────────────────────────────┤
|
|
│ │
|
|
│ ┌──────────────────────────────────────────────────────┐ │
|
|
│ │ PostgreSQL Database │ │
|
|
│ │ - content_items │ │
|
|
│ │ - media_resources │ │
|
|
│ │ - content_resource_relations │ │
|
|
│ └──────────────────────────────────────────────────────┘ │
|
|
│ │ │
|
|
│ ▼ │
|
|
│ ┌──────────────────────────────────────────────────────┐ │
|
|
│ │ Content Pack Generator │ │
|
|
│ │ - Genera pack-latest.json │ │
|
|
│ │ - Calcula hash SHA-256 │ │
|
|
│ └──────────────────────────────────────────────────────┘ │
|
|
│ │ │
|
|
│ ▼ │
|
|
│ ┌──────────────────────────────────────────────────────┐ │
|
|
│ │ API Endpoint │ │
|
|
│ │ GET /api/content-pack/latest.json │ │
|
|
│ └──────────────────────────────────────────────────────┘ │
|
|
└─────────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
---
|
|
|
|
## 🔧 Componentes Principales
|
|
|
|
### 1. ContentAdapter
|
|
|
|
**Ubicación:** `src/services/content-adapter.ts`
|
|
|
|
Interfaz que abstrae el acceso al contenido:
|
|
|
|
```typescript
|
|
interface ContentAdapter {
|
|
getProtocol(id: string): Procedure | null;
|
|
getDrug(id: string): Drug | null;
|
|
getGuide(id: string): Guide | null;
|
|
getAllProtocols(): Procedure[];
|
|
getAllDrugs(): Drug[];
|
|
getAllGuides(): Guide[];
|
|
isAvailable(): boolean;
|
|
}
|
|
```
|
|
|
|
#### LocalContentAdapter
|
|
|
|
- Usa datos locales (`procedures.ts`, `drugs.ts`, `guides-index.ts`)
|
|
- Siempre disponible
|
|
- Fallback garantizado
|
|
|
|
#### ExternalContentAdapter
|
|
|
|
- Carga Content Pack desde `/api/content-pack/latest.json`
|
|
- Cache en IndexedDB/localStorage
|
|
- Fallback automático a LocalContentAdapter si falla
|
|
|
|
#### ContentAdapterFactory
|
|
|
|
- Selecciona automáticamente el adapter disponible
|
|
- Prioriza ExternalContentAdapter si está disponible
|
|
- Fallback a LocalContentAdapter
|
|
|
|
---
|
|
|
|
### 2. Content Pack
|
|
|
|
**Ubicación Backend:** `backend/src/services/pack-generator.js`
|
|
**Endpoint:** `/api/content-pack/latest.json`
|
|
|
|
Estructura del Content Pack:
|
|
|
|
```json
|
|
{
|
|
"version": "1.0.0",
|
|
"generatedAt": "2025-01-06T12:00:00Z",
|
|
"hash": "sha256:...",
|
|
"content": {
|
|
"protocols": [...],
|
|
"guides": [...],
|
|
"drugs": [...],
|
|
"checklists": [...],
|
|
"manuals": [...]
|
|
},
|
|
"media": {
|
|
"images": [...],
|
|
"videos": [...]
|
|
},
|
|
"relations": [...]
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### 3. Sistema de Relaciones
|
|
|
|
**Ubicación:** `src/services/content-relations.ts`
|
|
|
|
Proporciona funciones para obtener relaciones bidireccionales:
|
|
|
|
- `getGuideForProtocol(protocolId)` - Obtiene guía relacionada
|
|
- `getProtocolForGuide(guideId)` - Obtiene protocolo relacionado
|
|
- `getProtocolRelations(protocolId)` - Obtiene todas las relaciones
|
|
- `getGuideRelations(guideId)` - Obtiene todas las relaciones
|
|
|
|
---
|
|
|
|
### 4. Sistema de Cache
|
|
|
|
**Ubicación:** `src/utils/indexeddb.ts`
|
|
|
|
#### IndexedDB (Prioritario)
|
|
|
|
- Mayor capacidad de almacenamiento
|
|
- Mejor rendimiento con packs grandes
|
|
- API asíncrona
|
|
|
|
#### localStorage (Fallback)
|
|
|
|
- Compatible con todos los navegadores
|
|
- Sincrónico
|
|
- Limitado a ~5-10MB
|
|
|
|
**Expiración:** 24 horas por defecto
|
|
|
|
---
|
|
|
|
## 🔄 Flujo de Datos
|
|
|
|
### Carga Inicial
|
|
|
|
1. App inicia → `ContentAdapterFactory` se inicializa
|
|
2. `ExternalContentAdapter` intenta cargar Content Pack:
|
|
- Primero desde IndexedDB (cache)
|
|
- Si no hay cache o está expirado, descarga desde API
|
|
- Guarda en cache
|
|
3. Si falla, usa `LocalContentAdapter` (datos locales)
|
|
|
|
### Uso en Páginas
|
|
|
|
1. Página necesita contenido → Llama a `contentAdapter.getProtocol(id)`
|
|
2. `ContentAdapterFactory` devuelve el adapter activo
|
|
3. Adapter busca en su fuente (pack o local)
|
|
4. Retorna contenido o `null` (fallback)
|
|
|
|
### Actualización del Pack
|
|
|
|
1. Backend genera nuevo pack → Guarda en `storage/content-pack/`
|
|
2. Frontend detecta nueva versión (hash diferente)
|
|
3. Descarga nuevo pack
|
|
4. Actualiza cache
|
|
|
|
---
|
|
|
|
## 🌐 API y Endpoints
|
|
|
|
### Content Pack
|
|
|
|
#### GET `/api/content-pack/latest.json`
|
|
|
|
Obtiene el Content Pack más reciente.
|
|
|
|
**Respuesta:**
|
|
```json
|
|
{
|
|
"version": "1.0.0",
|
|
"generatedAt": "2025-01-06T12:00:00Z",
|
|
"hash": "sha256:...",
|
|
"content": {...},
|
|
"media": {...}
|
|
}
|
|
```
|
|
|
|
**Headers:**
|
|
- `ETag`: Hash del pack
|
|
- `Cache-Control`: `public, max-age=3600`
|
|
|
|
#### GET `/api/content-pack/:version.json`
|
|
|
|
Obtiene una versión específica del Content Pack.
|
|
|
|
---
|
|
|
|
## 📦 Content Pack
|
|
|
|
### Generación
|
|
|
|
El Content Pack se genera automáticamente:
|
|
|
|
1. **On-the-fly:** Si no existe archivo, se genera al solicitar
|
|
2. **Programado:** Se puede generar periódicamente (cron job)
|
|
3. **Manual:** Desde admin panel
|
|
|
|
### Estructura de Contenido
|
|
|
|
Cada item en el pack tiene esta estructura:
|
|
|
|
```typescript
|
|
interface ContentItem {
|
|
id: string;
|
|
type: 'protocol' | 'guide' | 'drug' | 'checklist' | 'manual';
|
|
slug: string;
|
|
title: string;
|
|
shortTitle?: string;
|
|
description: string;
|
|
content: any; // Estructura específica por tipo
|
|
priority: 'critical' | 'high' | 'medium' | 'low';
|
|
status: 'draft' | 'in_review' | 'approved' | 'published';
|
|
version: string;
|
|
// ...
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 🔗 Enlaces Bidireccionales
|
|
|
|
### Mapeo
|
|
|
|
**Ubicación:** `src/data/protocol-guide-manual-mapping.ts`
|
|
|
|
Define las relaciones entre:
|
|
- Protocolos Operativos (Nivel 1)
|
|
- Guías de Refuerzo (Nivel 2)
|
|
- Manual Completo (Nivel 3)
|
|
|
|
### Uso en Páginas
|
|
|
|
```typescript
|
|
import { getProtocolRelations } from '@/services/content-relations';
|
|
|
|
const relations = getProtocolRelations('rcp-adulto-svb');
|
|
// Retorna: { protocol, guide, manual, mapping }
|
|
```
|
|
|
|
### Navegación
|
|
|
|
- **Desde Protocolo:** Enlaces a Guía y Manual
|
|
- **Desde Guía:** Enlaces a Protocolo y Manual
|
|
- **Desde Manual:** (Futuro) Enlaces a Protocolo y Guía
|
|
|
|
---
|
|
|
|
## 💾 Sistema de Cache
|
|
|
|
### IndexedDB
|
|
|
|
**Ventajas:**
|
|
- Mayor capacidad (GBs)
|
|
- Mejor rendimiento
|
|
- API asíncrona
|
|
|
|
**Uso:**
|
|
```typescript
|
|
import { saveContentPack, getContentPack } from '@/utils/indexeddb';
|
|
|
|
// Guardar
|
|
await saveContentPack(pack);
|
|
|
|
// Obtener
|
|
const cached = await getContentPack(24 * 60 * 60 * 1000); // 24 horas
|
|
```
|
|
|
|
### localStorage (Fallback)
|
|
|
|
Se usa automáticamente si IndexedDB no está disponible.
|
|
|
|
---
|
|
|
|
## 🧪 Testing
|
|
|
|
### Página de Testing
|
|
|
|
**Ruta:** `/testing`
|
|
|
|
Interfaz visual para ejecutar tests y ver resultados.
|
|
|
|
### Funciones de Testing
|
|
|
|
**Ubicación:** `src/utils/testing-helpers.ts`
|
|
|
|
Tests disponibles:
|
|
- `testContentAdapterAvailable()` - Verifica que el adapter está disponible
|
|
- `testGetProtocols()` - Verifica obtención de protocolos
|
|
- `testGetDrugs()` - Verifica obtención de fármacos
|
|
- `testGetGuides()` - Verifica obtención de guías
|
|
- `testProtocolToGuideRelations()` - Verifica relaciones
|
|
- `testContentPackCache()` - Verifica cache
|
|
|
|
### Ejecutar Tests
|
|
|
|
```typescript
|
|
import { runAllBasicTests, formatTestResults } from '@/utils/testing-helpers';
|
|
|
|
const results = runAllBasicTests();
|
|
console.log(formatTestResults(results));
|
|
```
|
|
|
|
---
|
|
|
|
## 📖 Guía de Uso
|
|
|
|
### Para Desarrolladores
|
|
|
|
#### Obtener Contenido
|
|
|
|
```typescript
|
|
import { getProtocol, getAllProtocols } from '@/services/content-adapter';
|
|
|
|
// Obtener protocolo específico
|
|
const protocol = getProtocol('rcp-adulto-svb');
|
|
|
|
// Obtener todos los protocolos
|
|
const protocols = getAllProtocols();
|
|
```
|
|
|
|
#### Usar Hooks
|
|
|
|
```typescript
|
|
import { useProtocolAdapter, useGuideAdapter } from '@/services/content-adapter';
|
|
|
|
function MyComponent() {
|
|
const { protocol, isLoading, isExternal } = useProtocolAdapter('rcp-adulto-svb');
|
|
|
|
if (isLoading) return <div>Cargando...</div>;
|
|
if (!protocol) return <div>No encontrado</div>;
|
|
|
|
return (
|
|
<div>
|
|
<h1>{protocol.title}</h1>
|
|
{isExternal && <span>Contenido Externo</span>}
|
|
</div>
|
|
);
|
|
}
|
|
```
|
|
|
|
#### Obtener Relaciones
|
|
|
|
```typescript
|
|
import { getProtocolRelations } from '@/services/content-relations';
|
|
|
|
const relations = getProtocolRelations('rcp-adulto-svb');
|
|
if (relations.guide) {
|
|
// Hay guía relacionada
|
|
}
|
|
if (relations.manual) {
|
|
// Hay manual relacionado
|
|
}
|
|
```
|
|
|
|
### Para Administradores
|
|
|
|
#### Generar Content Pack
|
|
|
|
1. Acceder al Admin Panel
|
|
2. Ir a "Content Pack"
|
|
3. Hacer clic en "Generar Pack"
|
|
4. El pack se genera y se guarda automáticamente
|
|
|
|
#### Verificar Cache
|
|
|
|
1. Abrir DevTools (F12)
|
|
2. Ir a Application → IndexedDB
|
|
3. Verificar `guia-tes-content` → `content-pack`
|
|
|
|
---
|
|
|
|
## 🔧 Troubleshooting
|
|
|
|
### El Content Pack no se carga
|
|
|
|
**Síntomas:**
|
|
- No aparece badge "Externo"
|
|
- Contenido siempre es local
|
|
|
|
**Soluciones:**
|
|
1. Verificar que el backend está corriendo
|
|
2. Verificar endpoint `/api/content-pack/latest.json`
|
|
3. Revisar consola del navegador para errores
|
|
4. Verificar que el pack se genera correctamente
|
|
|
|
### Cache no funciona
|
|
|
|
**Síntomas:**
|
|
- El pack se descarga cada vez
|
|
- No se guarda en IndexedDB
|
|
|
|
**Soluciones:**
|
|
1. Verificar permisos de IndexedDB en el navegador
|
|
2. Verificar que hay espacio disponible
|
|
3. Revisar consola para errores
|
|
4. El sistema fallback a localStorage automáticamente
|
|
|
|
### Enlaces bidireccionales no funcionan
|
|
|
|
**Síntomas:**
|
|
- Enlaces no aparecen
|
|
- Enlaces rotos
|
|
|
|
**Soluciones:**
|
|
1. Verificar que el mapeo existe en `protocol-guide-manual-mapping.ts`
|
|
2. Verificar que las IDs coinciden
|
|
3. Revisar consola para errores
|
|
4. Usar página de testing para verificar relaciones
|
|
|
|
---
|
|
|
|
## 📊 Estadísticas del Sistema
|
|
|
|
### Capacidad
|
|
|
|
- **IndexedDB:** Hasta varios GBs
|
|
- **localStorage:** ~5-10MB
|
|
- **Content Pack:** Típicamente < 5MB
|
|
|
|
### Rendimiento
|
|
|
|
- **Carga inicial:** < 500ms (con cache)
|
|
- **Carga sin cache:** 1-3s (depende del tamaño)
|
|
- **Búsqueda:** < 10ms (en memoria)
|
|
|
|
### Compatibilidad
|
|
|
|
- ✅ Chrome/Edge (IndexedDB + localStorage)
|
|
- ✅ Firefox (IndexedDB + localStorage)
|
|
- ✅ Safari (IndexedDB + localStorage)
|
|
- ✅ Mobile browsers (IndexedDB + localStorage)
|
|
|
|
---
|
|
|
|
## 🚀 Próximas Mejoras
|
|
|
|
1. **Compresión del Pack:** Comprimir JSON antes de enviar
|
|
2. **Differential Updates:** Solo actualizar cambios
|
|
3. **Service Worker:** Cache más agresivo
|
|
4. **Analytics:** Tracking de uso del contenido
|
|
5. **A/B Testing:** Múltiples versiones de contenido
|
|
|
|
---
|
|
|
|
## 📝 Notas Técnicas
|
|
|
|
### Seguridad
|
|
|
|
- El Content Pack es público (no contiene datos sensibles)
|
|
- Autenticación solo para admin panel
|
|
- Validación de contenido antes de publicar
|
|
|
|
### Escalabilidad
|
|
|
|
- El sistema puede manejar miles de items
|
|
- IndexedDB escala bien con grandes volúmenes
|
|
- El pack se puede dividir por categorías si crece mucho
|
|
|
|
### Mantenibilidad
|
|
|
|
- Código modular y desacoplado
|
|
- Fácil de extender con nuevos tipos de contenido
|
|
- Testing integrado
|
|
|
|
---
|
|
|
|
**Última actualización:** 2025-01-06
|
|
**Mantenido por:** Equipo de Desarrollo TES
|
|
|