feat: implementar set de calculadoras clínicas, unificar marca codigo0 y preparar infraestructura MongoDB
33
.github/workflows/deploy.yml
vendored
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
name: Deploy Código 0
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ main, master ]
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
cache: 'npm'
|
||||
cache-dependency-path: './frontend/package-lock.json'
|
||||
|
||||
- name: Deploy to VPS via Docker SSH
|
||||
uses: appleboy/ssh-action@v1.0.3
|
||||
with:
|
||||
host: ${{ secrets.SSH_HOST }}
|
||||
username: ${{ secrets.SSH_USER }}
|
||||
key: ${{ secrets.SSH_KEY }}
|
||||
passphrase: ${{ secrets.SSH_PASSPHRASE }} # Opcional si la clave tiene contraseña
|
||||
script: |
|
||||
cd /home/${{ secrets.SSH_USER }}/Proyectos/Proyectos\ en\ Desarrollo/codigo0-nuevo
|
||||
git pull origin main
|
||||
docker compose build
|
||||
docker compose up -d
|
||||
docker system prune -f # Limpieza opcional de imágenes huérfanas
|
||||
69
.gitignore
vendored
Executable file
|
|
@ -0,0 +1,69 @@
|
|||
# Dependencies
|
||||
node_modules
|
||||
admin-panel/node_modules/
|
||||
backend/node_modules/
|
||||
|
||||
# Environment variables
|
||||
.env
|
||||
.env.local
|
||||
.env.*.local
|
||||
backend/.env
|
||||
|
||||
# Build outputs
|
||||
dist
|
||||
build
|
||||
.admin-panel/dist
|
||||
backend/dist
|
||||
|
||||
# Runtime data
|
||||
*.log
|
||||
logs
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
.pnpm-debug.log*
|
||||
|
||||
# Coverage directory
|
||||
coverage
|
||||
.nyc_output
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/settings.json
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
*.swp
|
||||
*.swo
|
||||
*~
|
||||
|
||||
# OS generated files
|
||||
.DS_Store
|
||||
.DS_Store?
|
||||
._*
|
||||
.Spotlight-V100
|
||||
.Trashes
|
||||
ehthumbs.db
|
||||
Thumbs.db
|
||||
|
||||
# Contexto de IA
|
||||
.ai/
|
||||
*.ai.md
|
||||
.ai-assistant.md
|
||||
|
||||
# Documentación interna
|
||||
docs/internal/
|
||||
|
||||
# Archivos MD internos específicos
|
||||
docs/BACKLOG_MICRO_TICKETS.md
|
||||
docs/QUE_FALTA.md
|
||||
docs/CONTENIDO_FALTANTE.md
|
||||
SPEC.md
|
||||
README_TODO.md
|
||||
auditoria-assets-completa.md
|
||||
MEDIOS_REALES_NECESARIOS_FILTRADO.md
|
||||
|
||||
# Temporary files
|
||||
tmp
|
||||
temp
|
||||
175
.planning/INVENTARIO_COMPLETO.md
Normal file
|
|
@ -0,0 +1,175 @@
|
|||
# INVENTARIO COMPLETO - Proyecto Original vs Nuevo
|
||||
|
||||
## Resumen
|
||||
|
||||
**Total Bloques:** 18 directorios
|
||||
**Total Archivos MD:** ~100+
|
||||
|
||||
---
|
||||
|
||||
## 1. PROTOCOLOS TRANSTELEFÓNICOS (BLOQUE 5)
|
||||
|
||||
| # | Archivo MD | YAML | Estado Nuevo |
|
||||
|---|------------|------|--------------|
|
||||
| 1 | BLOQUE_05_1_PCR_TRANSTELEFONICA.md | ❌ | Falta |
|
||||
| 2 | BLOQUE_05_2_OVACE_TRANSTELEFONICA.md | ❌ | Falta |
|
||||
| 3 | BLOQUE_05_3_SCA_TRANSTELEFONICO.md | ❌ | Falta |
|
||||
| 4 | BLOQUE_05_4_ICTUS_TRANSTELEFONICO.md | ❌ | Falta |
|
||||
| 5 | BLOQUE_05_5_ANAFILAXIA_TRANSTELEFONICA.md | ❌ | Falta |
|
||||
| 6 | BLOQUE_05_6_CRISIS_ASMATICA_TRANSTELEFONICA.md | ❌ | Falta |
|
||||
| 7 | BLOQUE_05_7_HIPOGLUCEMIA_TRANSTELEFONICA.md | ❌ | Falta |
|
||||
| 8 | BLOQUE_05_8_COMUNICACION_COORDINADOR.md | ❌ | Falta |
|
||||
| 9 | BLOQUE_05_9_PROTOCOLOS_EMERGENCIAS_ESPECIFICAS.md | ❌ | Falta |
|
||||
|
||||
**→ Total Transtelefónicos: 9 protocolos**
|
||||
|
||||
---
|
||||
|
||||
## 2. SOPORTE VITAL Y RCP (BLOQUE 4)
|
||||
|
||||
| # | Archivo MD | Página Nueva | Estado |
|
||||
|---|------------|--------------|--------|
|
||||
| 1 | BLOQUE_04_1_RCP_ADULTOS.md | RCP.tsx | ⚠️ Mockeado |
|
||||
| 2 | BLOQUE_04_2_RCP_PEDIATRIA.md | RCP.tsx | ❌ Falta |
|
||||
| 3 | BLOQUE_04_3_RCP_LACTANTES.md | RCP.tsx | ❌ Falta |
|
||||
| 4 | BLOQUE_04_4_USO_DESA.md | - | ❌ Falta |
|
||||
| 5 | BLOQUE_04_5_RCP_DOS_INTERVINIENTES.md | - | ❌ Falta |
|
||||
| 6 | BLOQUE_04_6_OVACE_ADULTOS.md | - | ❌ Falta |
|
||||
| 7 | BLOQUE_04_7_OVACE_PEDIATRIA.md | - | ❌ Falta |
|
||||
| 8 | BLOQUE_04_8_OVACE_LACTANTES.md | - | ❌ Falta |
|
||||
| 9 | BLOQUE_04_9_POSICION_LATERAL_SEGURIDAD.md | - | ❌ Falta |
|
||||
| 10 | BLOQUE_04_0_RECONOCIMIENTO_PCR.md | - | ❌ Falta |
|
||||
| 11 | BLOQUE_04_10_ACCESO_VASCULAR_BASICO.md | - | ❌ Falta |
|
||||
|
||||
**→ Total Soporte Vital: 11 guías/protocolos**
|
||||
|
||||
---
|
||||
|
||||
## 3. PROCEDIMIENTOS BÁSICOS (BLOQUE 1)
|
||||
|
||||
| # | Archivo MD | Página Nueva | Estado |
|
||||
|---|------------|--------------|--------|
|
||||
| 1 | BLOQUE_01_1_CONSTANTES_VITALES.md | Escena.tsx | ❌ Falta |
|
||||
| 2 | BLOQUE_01_2_ABCDE_OPERATIVO.md | Escena.tsx | ❌ Falta |
|
||||
| 3 | BLOQUE_01_3_GLASGOW_OPERATIVO.md | Herramientas.tsx | ✅ Calculadora |
|
||||
| 4 | BLOQUE_01_4_TRIAGE_START.md | - | ❌ Falta |
|
||||
|
||||
**→ Total Procedimientos: 4**
|
||||
|
||||
---
|
||||
|
||||
## 4. MATERIAL E INMOVILIZACIÓN (BLOQUE 2)
|
||||
|
||||
| # | Archivo MD | Página Nueva | Estado |
|
||||
|---|------------|--------------|--------|
|
||||
| 1 | BLOQUE_02_0_ANATOMIA_OPERATIVA.md | - | ❌ Falta |
|
||||
| 2 | BLOQUE_02_2_INMOVILIZACION_MANUAL.md | Material.tsx | ❌ Falta |
|
||||
| 3 | BLOQUE_02_3_COLLARIN_CERVICAL.md | ViaAerea.tsx | ❌ Falta |
|
||||
| 4 | BLOQUE_02_4_CAMILLA_CUCHARA.md | Material.tsx | ❌ Falta |
|
||||
| 5 | BLOQUE_02_5_TABLERO_ESPINAL.md | Material.tsx | ❌ Falta |
|
||||
| 6 | BLOQUE_02_6_COLCHON_VACIO.md | Material.tsx | ❌ Falta |
|
||||
| 7 | BLOQUE_02_7_EXTRICACION_MOVIMIENTOS_BLOQUE.md | - | ❌ Falta |
|
||||
| 8 | BLOQUE_02_8_TRANSFERENCIAS_MOVILIZACION.md | - | ❌ Falta |
|
||||
| 9 | BLOQUE_02_9_ERRORES_CRITICOS.md | - | ❌ Falta |
|
||||
| 10 | BLOQUE_02_10_FERULAS.md | - | ❌ Falta |
|
||||
| 11 | BLOQUE_02_11_CINTURON_PELVICO.md | - | ❌ Falta |
|
||||
| 12 | BLOQUE_02_12_FERULA_TRACCION.md | - | ❌ Falta |
|
||||
| 13 | BLOQUE_02_13_CAMILLAS_SILLAS_EVACUACION.md | - | ❌ Falta |
|
||||
| 14 | BLOQUE_02_14_INVENTARIO_MATERIAL.md | - | ❌ Falta |
|
||||
|
||||
**→ Total Material: 14**
|
||||
|
||||
---
|
||||
|
||||
## 5. MATERIAL SANITARIO Y OXIGENOTERAPIA (BLOQUE 3)
|
||||
|
||||
| # | Archivo MD | Estado |
|
||||
|---|------------|--------|
|
||||
| 1-4 | Oxigenoterapia | ❌ Falta |
|
||||
| 5-9 | Dispositivos, Aspiración, BVM | ❌ Falta |
|
||||
| 10-13 | Monitorización, Glucómetro, Termometría | ❌ Falta |
|
||||
| 14-18 | Bioseguridad, Gestión, Documentación | ❌ Falta |
|
||||
| 19-24 | Maletines, Inventarios | ❌ Falta |
|
||||
|
||||
**→ Total Material Sanitario: ~24**
|
||||
|
||||
---
|
||||
|
||||
## 6. FARMACOLOGÍA (BLOQUE 6)
|
||||
|
||||
| # | Archivo MD | Página Nueva | Estado |
|
||||
|---|------------|--------------|--------|
|
||||
| 1 | BLOQUE_06_0_PRINCIPIOS_ADMINISTRACION_FARMACOS.md | - | ❌ Falta |
|
||||
| 2 | BLOQUE_06_1_VADEMECUM_OPERATIVO.md | Farmacos.tsx | ✅ Existe |
|
||||
| 3 | BLOQUE_06_2_OXIGENO_ADMINISTRACION_Y_SEGURIDAD.md | - | ❌ Falta |
|
||||
| 4 | BLOQUE_06_3_ADRENALINA_USO_ANAFILAXIA_Y_RCP.md | - | ❌ Falta |
|
||||
| 5 | BLOQUE_06_4_ASPIRINA_USO_SCA.md | - | ❌ Falta |
|
||||
| 6 | BLOQUE_06_5_GLUCAGON_USO_HIPOGLUCEMIA.md | - | ❌ Falta |
|
||||
| 7 | BLOQUE_06_6_SALBUTAMOL_USO_CRISIS_ASMATICA.md | - | ❌ Falta |
|
||||
| 8 | BLOQUE_06_7_ABREVIATURAS_TERMINOLOGIA_FARMACOLOGICA.md | - | ❌ Falta |
|
||||
|
||||
**→ Total Farmacología: 8**
|
||||
|
||||
---
|
||||
|
||||
## 7. OTRAS GUÍAS
|
||||
|
||||
| Bloque | Área | Archivos |
|
||||
|--------|------|----------|
|
||||
| BLOQUE 0 | Fundamentos | 1 |
|
||||
| BLOQUE 7 | Conducción y Seguridad Vial | 5 |
|
||||
| BLOQUE 8 | Gestión Operativa | 4 |
|
||||
| BLOQUE 9 | Medicina Emergencias | 1 |
|
||||
| BLOQUE 10 | Situaciones Especiales | 1 |
|
||||
| BLOQUE 11 | Protocolos Trauma | 1 |
|
||||
| BLOQUE 12 | Marco Legal | 1 |
|
||||
| BLOQUE 13 | Comunicación | 1 |
|
||||
| BLOQUE 14 | Seguridad Personal | 1 |
|
||||
| BLOQUE 15 | Alteraciones Psiquiátricas | 6 |
|
||||
|
||||
---
|
||||
|
||||
## RESUMEN TOTAL
|
||||
|
||||
| Categoría | Total Archivos | En Proyecto Nuevo |
|
||||
|-----------|----------------|-------------------|
|
||||
| Transtelefónicos | 9 | 1 (solo rcp-adulto) |
|
||||
| Soporte Vital/RCP | 11 | 1 (mockeado) |
|
||||
| Procedimientos Básicos | 4 | 1 parcial |
|
||||
| Material/Inmovilización | 14 | 1 parcial |
|
||||
| Material Sanitario | ~24 | 1 parcial |
|
||||
| Farmacología | 8 | 1 |
|
||||
| Otros Bloques | ~22 | 0 |
|
||||
|
||||
**TOTAL: ~100+ protocolos/guías → En nuevo: ~15 páginas**
|
||||
|
||||
---
|
||||
|
||||
## RECURSOS VISUALES - IMÁGENES/INFOGRAFÍAS
|
||||
|
||||
**Ubicación en original:** `/public/assets/infografias/`
|
||||
|
||||
### Por Bloque:
|
||||
| Bloque | Carpetas | Contenido |
|
||||
|--------|----------|-----------|
|
||||
| bloque-0-fundamentos | ✅ | Fundamentos |
|
||||
| bloque-2-inmovilizacion | ✅ | Collarines, tablas, férulas |
|
||||
| bloque-3-material-sanitario | ✅ | Material sanitario |
|
||||
| bloque-4-rcp | ✅ | Algoritmo RCP (svg + png) |
|
||||
| bloque-7-conduccion | ✅ | Conducción |
|
||||
| bloque-12-marco-legal | ✅ | Legal |
|
||||
|
||||
**Total carpetas con infografías: 6**
|
||||
|
||||
### Imágenes específicas bloque-4-rcp:
|
||||
- algoritmo_rcp_comentado.png
|
||||
- algoritmo_rcp_comentado.svg
|
||||
- introduccion_rcp_adulto_svb.png
|
||||
|
||||
---
|
||||
|
||||
## RECURSOS VISUALES - VIDEOS
|
||||
|
||||
**Buscar en archivos MD:** patrones `<video>`, `youtube`, `embed`
|
||||
|
||||
*(Pendiente de análisis)*
|
||||
128
.planning/PHASE-PLAN.md
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
# Código0 Nuevo - Plan de Fases para Guías y Protocolos
|
||||
|
||||
## Estado Actual (15/03/2026)
|
||||
|
||||
### ✅ Completado
|
||||
- Sistema Visual Protocol Renderer (YAML + componentes React)
|
||||
- 69 archivos YAML de protocolos creados
|
||||
- Hook `useProtocol.ts` funcionando
|
||||
- VisualProtocolRenderer integrado en página Protocolo
|
||||
- Infografías copiadas (6 carpetas)
|
||||
|
||||
---
|
||||
|
||||
## 📋 INVENTARIO COMPLETO
|
||||
|
||||
### 1. PROTOCOLOS TRANSTELEFÓNICOS (BLOQUE 5) - 13 total
|
||||
|
||||
| # | Archivo | YAML | Estado |
|
||||
|---|---------|------|--------|
|
||||
| 1 | PCR Adulto | ✅ | Completado |
|
||||
| 2 | PCR Pediatría | ✅ | Completado |
|
||||
| 3 | PCR Lactantes | ✅ | Completado |
|
||||
| 4 | OVACE Adulto | ✅ | Completado |
|
||||
| 5 | OVACE Lactantes | ✅ | Completado |
|
||||
| 6 | DESA Teléfono | ✅ | Completado |
|
||||
| 7 | SCA | ✅ | Completado |
|
||||
| 8 | Ictus | ✅ | Completado |
|
||||
| 9 | Anafilaxia | ✅ | Completado |
|
||||
| 10 | Crisis Asmática | ✅ | Completado |
|
||||
| 11 | Hipoglucemia | ✅ | Completado |
|
||||
| 12 | Comunicación Coordinador | ✅ | Completado |
|
||||
| 13 | RCP Clínica (no telefónico) | ✅ | Ya existía |
|
||||
|
||||
### 2. SOPORTE VITAL Y RCP (BLOQUE 4) - 11 total
|
||||
|
||||
| # | Archivo | Estado |
|
||||
|---|---------|--------|
|
||||
| 1 | RCP Adultos | ✅ Completado |
|
||||
| 2 | RCP Pediatría | ✅ Completado |
|
||||
| 3 | RCP Lactantes | ✅ Completado |
|
||||
| 4 | Uso DESA | ✅ Completado |
|
||||
| 5 | RCP 2 Intervinientes | ✅ Completado |
|
||||
| 6 | OVACE Adultos | ✅ Completado |
|
||||
| 7 | OVACE Pediatría | ✅ Completado |
|
||||
| 8 | OVACE Lactantes | ✅ Completado |
|
||||
| 9 | Posición Lateral Seguridad | ✅ Completado |
|
||||
| 10 | Reconocimiento PCR | ✅ Completado |
|
||||
| 11 | Acceso Vascular Básico | ✅ Completado |
|
||||
|
||||
### 3. PROCEDIMIENTOS BÁSICOS (BLOQUE 1) - 4 total
|
||||
- Constantes Vitales - ✅ Completado
|
||||
- ABCDE Operativo - ✅ Completado
|
||||
- Glasgow - ✅ En calculadoras
|
||||
- Triage START - ✅ Completado
|
||||
|
||||
### 4. MATERIAL E INMOVILIZACIÓN (BLOQUE 2) - 14 total
|
||||
-Todos faltantes
|
||||
|
||||
### 5. MATERIAL SANITARIO (BLOQUE 3) - ~24 total
|
||||
- Todos faltantes
|
||||
|
||||
### 6. FARMACOLOGÍA (BLOQUE 6) - 8 total
|
||||
- Vademécum operativo - ✅ Existe
|
||||
|
||||
### 7. OTROS BLOQUES - ~22 total
|
||||
- Todos faltantes
|
||||
|
||||
---
|
||||
|
||||
## RECURSOS VISUALES
|
||||
|
||||
### Imágenes/Infografías
|
||||
**Origen:** `/Proyectos Originales/codigo0/public/assets/infografias/`
|
||||
|
||||
| Carpeta | Contenido |
|
||||
|---------|-----------|
|
||||
| bloque-0-fundamentos | Fundamentos |
|
||||
| bloque-2-inmovilizacion | Collarines, tablas, férulas |
|
||||
| bloque-3-material-sanitario | Material sanitario |
|
||||
| bloque-4-rcp | Algoritmo RCP (svg + png) |
|
||||
| bloque-7-conduccion | Conducción |
|
||||
| bloque-12-marco-legal | Legal |
|
||||
|
||||
**Acción:** Copiar a `/frontend/public/assets/infografias/`
|
||||
|
||||
### Videos
|
||||
*Pendiente análisis*
|
||||
|
||||
---
|
||||
|
||||
## PLAN DE FASES PROPUESTO
|
||||
|
||||
### Phase 1: Transtelefónicos (13 protocolos YAML)
|
||||
**Objetivo:** Crear los protocolos transtelefónicos en YAML
|
||||
**Estado:** ✅ COMPLETADO (15/03/2026)
|
||||
|
||||
### Phase 2: Soporte Vital RCP (11 guías clínicas)
|
||||
**Objetivo:** Convertir todas las guías de soporte vital a YAML
|
||||
**Estado:** ✅ COMPLETADO (15/03/2026)
|
||||
|
||||
### Phase 3: Procedimientos Básicos (4 guías)
|
||||
**Objetivo:** ABCDE, Constantes, Glasgow, Triage
|
||||
**Estado:** ✅ COMPLETADO (15/03/2026)
|
||||
|
||||
### Phase 4: Farmacología (8 fármacos)
|
||||
**Objetivo:** Vademécum completo en YAML
|
||||
**Estado:** ✅ COMPLETADO (6 fármacos)
|
||||
|
||||
### Phase 5: Material e Inmovilización (14 guías)
|
||||
**Estado:** ✅ COMPLETADO (12 guías)
|
||||
|
||||
### Phase 6: Material Sanitario (~24 guías)
|
||||
**Objetivo:**Oxigenoterapia, dispositivos, monitorización
|
||||
|
||||
### Phase 7: Otros Bloques (~22 guías)
|
||||
**Objetivo:** Conducción, gestión, legal, psiquiatría
|
||||
|
||||
### Phase 8: Recursos Visuales - Imágenes
|
||||
**Objetivo:** Copiar infografías del original
|
||||
**Estado:** ✅ COMPLETADO (15/03/2026)
|
||||
|
||||
### Phase 9: Recursos Visuales - Videos (Optional)
|
||||
**Objetivo:**Evaluar e implementar videos
|
||||
|
||||
---
|
||||
|
||||
*Plan creado: 2026-03-15*
|
||||
*Proyecto: Código0 Nuevo*
|
||||
160
.planning/ROADMAP.md
Normal file
|
|
@ -0,0 +1,160 @@
|
|||
# Código 0 - Roadmap GSD
|
||||
|
||||
## Milestone v1.0: Minimum Viable Product
|
||||
|
||||
### Phase 01: Infraestructura Core
|
||||
**Goal:** Sistema de rendering de protocolos y base del frontend
|
||||
|
||||
| # | Plan | Status |
|
||||
|---|------|--------|
|
||||
| 01-01 | Setup proyecto React+Vite con routing | ✅ |
|
||||
| 01-02 | Crear VisualProtocolRenderer | ✅ |
|
||||
| 01-03 | Implementar useProtocol hook | ✅ |
|
||||
|
||||
**Goal:** Backend API básica
|
||||
|
||||
| # | Plan | Status |
|
||||
|---|------|--------|
|
||||
| 01-04 | Setup Express server | ✅ |
|
||||
| 01-05 | Crear API de protocolos transtelefónicos | ⚠️ partial |
|
||||
|
||||
---
|
||||
|
||||
### Phase 02: Contenido Clínico - Transtelefónicos
|
||||
**Goal:** 13 protocolos transtelefónicos en YAML
|
||||
|
||||
| # | Plan | Status |
|
||||
|---|------|--------|
|
||||
| 02-01 | PCR Adulto/Pediatria/Lactantes | ✅ |
|
||||
| 02-02 | OVACE Adulto/Pediatria/Lactantes | ✅ |
|
||||
| 02-03 | SCA, Ictus, Anafilaxia, Crisis Asmática | ✅ |
|
||||
| 02-04 | Hipoglucemia, DESA, Comunicación Coordinador | ✅ |
|
||||
|
||||
---
|
||||
|
||||
### Phase 03: Soporte Vital y RCP
|
||||
**Goal:** 11 guías de soporte vital en YAML
|
||||
|
||||
| # | Plan | Status |
|
||||
|---|------|--------|
|
||||
| 03-01 | RCP Adultos/Pediatria/Lactantes | ✅ |
|
||||
| 03-02 | OVACE y uso DESA | ✅ |
|
||||
| 03-03 | RCP 2 intervinientes, Posición Lateral | ✅ |
|
||||
| 03-04 | Reconocimiento PCR, Acceso Vascular | ✅ |
|
||||
|
||||
---
|
||||
|
||||
### Phase 04: Procedimientos Básicos
|
||||
**Goal:** ABCDE, Constantes, Glasgow, Triage
|
||||
|
||||
| # | Plan | Status |
|
||||
|---|------|--------|
|
||||
| 04-01 | Constantes Vitales y ABCDE Operativo | ✅ |
|
||||
| 04-02 | Glasgow (integrado en calculadoras) | ✅ |
|
||||
| 04-03 | Triage START | ✅ |
|
||||
|
||||
---
|
||||
|
||||
### Phase 05: Farmacología
|
||||
**Goal:** Vademécum operativo en YAML
|
||||
|
||||
| # | Plan | Status |
|
||||
|---|------|--------|
|
||||
| 05-01 | Adrenalina, Aspirina, Salbutamol | ✅ |
|
||||
| 05-02 | Oxígeno, Glucagon, Principios administración | ✅ |
|
||||
| 05-03 | Integración en Farmacos.tsx | ✅ |
|
||||
|
||||
---
|
||||
|
||||
### Phase 06: Material e Inmovilización
|
||||
**Goal:** 14 guías de inmovilización
|
||||
|
||||
| # | Plan | Status |
|
||||
|---|------|--------|
|
||||
| 06-01 | Collarines, Tabla Espinal | ✅ |
|
||||
| 06-02 | Férulas, Inmovilización completa | ✅ |
|
||||
|
||||
---
|
||||
|
||||
### Phase 07: Material Sanitario
|
||||
**Goal:** ~24 guías de material sanitario
|
||||
|
||||
| # | Plan | Status |
|
||||
|---|------|--------|
|
||||
| 07-01 | Oxigenoterapia | ✅ |
|
||||
| 07-02 | Termometría, Bioseguridad | ✅ |
|
||||
| 07-03 | Otros dispositivos | 🔄 in progress |
|
||||
|
||||
---
|
||||
|
||||
### Phase 08: Páginas Esenciales
|
||||
**Goal:** Páginas que faltan del frontend
|
||||
|
||||
| # | Plan | Status |
|
||||
|---|------|--------|
|
||||
| 08-01 | Favoritos.tsx + useFavorites | 🔄 pending |
|
||||
| 08-02 | Historial.tsx + useHistory | 🔄 pending |
|
||||
| 08-03 | Urgencias.tsx | 🔄 pending |
|
||||
| 08-04 | Parto.tsx + protocolo YAML | 🔄 pending |
|
||||
| 08-05 | AvisoLegal + DescargoResponsabilidad | 🔄 pending |
|
||||
|
||||
---
|
||||
|
||||
### Phase 09: Features Offline y Búsqueda
|
||||
**Goal:** Funcionalidad offline y búsqueda
|
||||
|
||||
| # | Plan | Status |
|
||||
|---|------|--------|
|
||||
| 09-01 | useSearch - búsqueda full-text | 🔄 pending |
|
||||
| 09-02 | useOfflineMode - PWA + caché | 🔄 pending |
|
||||
| 09-03 | Service Worker | 🔄 pending |
|
||||
|
||||
---
|
||||
|
||||
### Phase 10: Componentes UI Avanzados
|
||||
**Goal:** Componentes mejorados
|
||||
|
||||
| # | Plan | Status |
|
||||
|---|------|--------|
|
||||
| 10-01 | MarkdownViewer | 🔄 pending |
|
||||
| 10-02 | DecisionTreeViewer | 🔄 pending |
|
||||
| 10-03 | InfografiaViewer | 🔄 pending |
|
||||
|
||||
---
|
||||
|
||||
## Milestone v2.0: Contenido Completo
|
||||
|
||||
### Phase 11: Bloques 7-15 del Manual
|
||||
**Goal:** 75+ capítulos restantes
|
||||
|
||||
| # | Plan | Status |
|
||||
|---|------|--------|
|
||||
| 11-01 | Bloque 7: Conducción y Seguridad Vial | 🔄 pending |
|
||||
| 11-02 | Bloque 8: Gestión Operativa | 🔄 pending |
|
||||
| 11-03 | Bloque 9: Medicina de Emergencias | 🔄 pending |
|
||||
| 11-04 | Bloque 10-15: Restantes | 🔄 pending |
|
||||
|
||||
---
|
||||
|
||||
### Phase 12: Guías de Refuerzo JSON
|
||||
**Goal:** 10 guías de refuerzo
|
||||
|
||||
| # | Plan | Status |
|
||||
|---|------|--------|
|
||||
| 12-01 | Migrar 10 guías JSON desde original | 🔄 pending |
|
||||
|
||||
---
|
||||
|
||||
### Phase 13: Integración Backend Completa
|
||||
**Goal:** Conectar frontend completamente al backend
|
||||
|
||||
| # | Plan | Status |
|
||||
|---|------|--------|
|
||||
| 13-01 | Completar 3 protocolos transtelefónicos faltantes | 🔄 pending |
|
||||
| 13-02 | API de favoritos e historial | 🔄 pending |
|
||||
| 13-03 | API de búsqueda | 🔄 pending |
|
||||
|
||||
---
|
||||
|
||||
*Generado desde PHASE-PLAN.md y codigo0-roadmap-maestro.md*
|
||||
*Proyecto: Código 0 - codigo0-nuevo*
|
||||
42
.planning/STATE.md
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
# Estado - Código 0
|
||||
|
||||
## Project State
|
||||
**status:** in_progress
|
||||
**last_updated:** 2026-03-17
|
||||
|
||||
## Resumen Ejecutivo
|
||||
|
||||
El nuevo codigo0-nuevo tiene la **infraestructura técnica lista** pero le falta contenido clínico y páginas esenciales.
|
||||
|
||||
## Métricas
|
||||
|
||||
| Métrica | Total | Completado | % |
|
||||
|---------|-------|------------|---|
|
||||
| YAMLs de protocolos | ~90 | 69 | 77% |
|
||||
| Páginas frontend | 26 | 22 | 85% |
|
||||
| Fases GSD | 13 | 6 | 46% |
|
||||
|
||||
## Decisiones
|
||||
|
||||
| Decisión | rationale |
|
||||
|----------|-----------|
|
||||
| Formato YAML para protocolos | Renderer ya implementado, fácil de mantener |
|
||||
| Frontend-only con PWA | Sin app nativa, offline-capable |
|
||||
| No Firebase | VPS con PostgreSQL disponible |
|
||||
| Backend Express separado | Arquitectura modular |
|
||||
|
||||
## Blockers
|
||||
|
||||
| Bloqueador | severidad |
|
||||
|------------|-----------|
|
||||
| Falta conectar frontend a backend | high |
|
||||
| No hay persistencia de favoritos/historial | high |
|
||||
| PWA no implementado | medium |
|
||||
|
||||
## Próxima Fase
|
||||
|
||||
**Phase 08: Páginas Esenciales** - Las páginas que faltan (Favoritos, Historial, Urgencias, Parto)
|
||||
|
||||
---
|
||||
|
||||
*Generado automáticamente*
|
||||
239
.planning/codebase/ARCHITECTURE.md
Normal file
|
|
@ -0,0 +1,239 @@
|
|||
# Architecture
|
||||
|
||||
**Analysis Date:** 2026-03-13
|
||||
|
||||
## Pattern Overview
|
||||
|
||||
**Overall:** Modular Monorepo with Frontend-Backend Separation
|
||||
|
||||
**Key Characteristics:**
|
||||
- **Separation of Concerns**: Frontend and backend are completely separate applications with no shared runtime
|
||||
- **API-First Design**: Backend exposes RESTful API endpoints consumed by the frontend
|
||||
- **Component-Based UI**: React application structured by feature domains (clinical, protocols, tools)
|
||||
- **Lazy Loading**: Heavy pages are lazy-loaded for performance optimization
|
||||
|
||||
## Layers
|
||||
|
||||
### Backend Layer
|
||||
|
||||
**Purpose:** API server providing medical protocol data and authentication
|
||||
**Location:** `backend/`
|
||||
**Contains:**
|
||||
- Express server setup (`src/index.ts`, `src/app.ts`)
|
||||
- Route handlers (`src/routes/`)
|
||||
- Business logic/services (`src/services/`)
|
||||
- Configuration (`src/config.ts`)
|
||||
- TypeScript types/interfaces
|
||||
|
||||
**Depends on:**
|
||||
- Express framework for HTTP handling
|
||||
- Mongoose for MongoDB connectivity (configured but placeholder)
|
||||
- JWT for authentication
|
||||
- Zod for validation (not yet implemented in routes)
|
||||
|
||||
**Used by:**
|
||||
- Frontend application via HTTP API calls
|
||||
|
||||
### Frontend Layer
|
||||
|
||||
**Purpose:** React single-page application for medical protocol reference
|
||||
**Location:** `frontend/src/`
|
||||
**Contains:**
|
||||
- Application entry point (`main.tsx`)
|
||||
- Root component with routing (`App.tsx`)
|
||||
- Page components (`pages/`)
|
||||
- UI components (`components/`)
|
||||
- Layout components (`components/layout/`)
|
||||
- Services layer (`services/`)
|
||||
- Types definitions (`types/`)
|
||||
- Utilities (`utils/`, `hooks/`)
|
||||
|
||||
**Depends on:**
|
||||
- React 18 with hooks
|
||||
- React Router DOM for navigation
|
||||
- TailwindCSS for styling
|
||||
- Next-themes for dark mode
|
||||
- Lucide React for icons
|
||||
|
||||
**Used by:**
|
||||
- End users via web browser
|
||||
|
||||
### Static Site Layer
|
||||
|
||||
**Purpose:** Marketing/promotional landing page
|
||||
**Location:** `promo-site/`
|
||||
**Contains:**
|
||||
- Static HTML with inline CSS
|
||||
- Marketing content and calls-to-action
|
||||
|
||||
## Data Flow
|
||||
|
||||
### API Request Flow
|
||||
|
||||
1. **Frontend Component** triggers data fetch (e.g., `pages/Index.tsx`)
|
||||
2. **Service Layer** (`frontend/src/services/`) makes HTTP request to backend
|
||||
3. **Backend Route** (`backend/src/routes/`) receives request
|
||||
4. **Service Layer** (`backend/src/services/`) processes business logic
|
||||
5. **Response** returns JSON data to frontend
|
||||
6. **Component** renders data using React state
|
||||
|
||||
### Protocol Data Flow (Example: RCP Protocol)
|
||||
|
||||
```
|
||||
Frontend: Protocol Page
|
||||
↓ HTTP GET /api/content/:id
|
||||
Backend: content.ts route
|
||||
↓ calls telephone-protocols.ts service
|
||||
Backend: telephone-protocols.ts service
|
||||
↓ returns TelephoneProtocol object
|
||||
Backend: JSON response
|
||||
↓
|
||||
Frontend: Service layer
|
||||
↓ state update
|
||||
Frontend: Component renders protocol steps
|
||||
```
|
||||
|
||||
## State Management
|
||||
|
||||
**Frontend State:**
|
||||
- **Local State**: Component-level state using `useState` for UI interactions (search modal, menu state)
|
||||
- **URL State**: React Router for navigation and route parameters (`/protocolo/:id`)
|
||||
- **Theme State**: `next-themes` for dark/light mode persistence
|
||||
|
||||
**Backend State:**
|
||||
- **Configuration**: Environment variables loaded via `dotenv`
|
||||
- **Session State**: JWT tokens (authentication flow placeholder)
|
||||
- **Database State**: MongoDB connection (configured but not fully implemented)
|
||||
|
||||
## Key Abstractions
|
||||
|
||||
### Telephone Protocol Model
|
||||
|
||||
**Purpose:** Standardized structure for medical emergency protocols
|
||||
**Location:** `backend/src/services/telephone-protocols.ts`
|
||||
**Examples:**
|
||||
- `rcpTelephoneAdult` - Adult CPR protocol
|
||||
- `ictusTelephone` - Stroke assessment protocol
|
||||
- `desaTelephone` - AED usage protocol
|
||||
|
||||
**Pattern:**
|
||||
```typescript
|
||||
interface TelephoneProtocol {
|
||||
id: string;
|
||||
title: string;
|
||||
category: ProtocolCategory;
|
||||
steps: ProtocolStep[];
|
||||
initialAssessment: string[];
|
||||
importantNotes?: string[];
|
||||
}
|
||||
```
|
||||
|
||||
### API Route Pattern
|
||||
|
||||
**Purpose:** Consistent Express route structure
|
||||
**Location:** `backend/src/routes/`
|
||||
**Pattern:**
|
||||
```typescript
|
||||
import { Router } from 'express';
|
||||
const router = Router();
|
||||
|
||||
router.get('/', (req, res) => { /* ... */ });
|
||||
router.get('/:id', (req, res) => { /* ... */ });
|
||||
router.post('/', (req, res) => { /* ... */ });
|
||||
|
||||
export default router;
|
||||
```
|
||||
|
||||
### React Page Pattern
|
||||
|
||||
**Purpose:** Consistent page component structure
|
||||
**Location:** `frontend/src/pages/`
|
||||
**Pattern:**
|
||||
```typescript
|
||||
import { useState } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
// ... imports
|
||||
|
||||
const PageName = () => {
|
||||
// State and logic
|
||||
return (
|
||||
<div>
|
||||
{/* JSX */}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default PageName;
|
||||
```
|
||||
|
||||
## Entry Points
|
||||
|
||||
### Backend Entry Point
|
||||
|
||||
**Location:** `backend/src/index.ts`
|
||||
**Triggers:** Node.js process execution
|
||||
**Responsibilities:**
|
||||
- Create Express app instance
|
||||
- Read configuration
|
||||
- Start HTTP server on configured port
|
||||
- Handle graceful shutdown signals (SIGINT, SIGTERM)
|
||||
|
||||
### Frontend Entry Point
|
||||
|
||||
**Location:** `frontend/src/main.tsx`
|
||||
**Triggers:** Browser loads HTML page
|
||||
**Responsibilities:**
|
||||
- Initialize React root
|
||||
- Register Service Worker (production only)
|
||||
- Render main App component with error boundaries
|
||||
- Apply console error filtering for browser extensions
|
||||
|
||||
### Frontend App Entry
|
||||
|
||||
**Location:** `frontend/src/App.tsx`
|
||||
**Triggers:** React root render
|
||||
**Responsibilities:**
|
||||
- Set up ThemeProvider for dark mode
|
||||
- Configure BrowserRouter with future flags
|
||||
- Define all application routes with lazy loading
|
||||
- Layout structure (Header, Main, Footer, BottomNav)
|
||||
- Modal management (Search, Menu)
|
||||
|
||||
## Error Handling
|
||||
|
||||
**Strategy:** Layered error handling with graceful degradation
|
||||
|
||||
**Patterns:**
|
||||
|
||||
**Frontend Error Boundaries:**
|
||||
- Root-level error display in `main.tsx`
|
||||
- Suspense fallbacks for lazy-loaded pages
|
||||
- Console error filtering for non-critical browser extension errors
|
||||
|
||||
**Backend Error Handling:**
|
||||
- Global error handler in `app.ts` (line 51-57)
|
||||
- 404 handler for undefined routes
|
||||
- Development mode shows detailed error messages
|
||||
- Production mode shows generic error messages
|
||||
|
||||
## Cross-Cutting Concerns
|
||||
|
||||
**Logging:**
|
||||
- **Backend:** Console logging for server startup, errors, and graceful shutdown
|
||||
- **Frontend:** Console logging for Service Worker registration, React rendering errors
|
||||
|
||||
**Validation:**
|
||||
- **Backend:** Zod dependency available but not yet integrated into routes
|
||||
- **Frontend:** No explicit validation layer detected
|
||||
|
||||
**Authentication:**
|
||||
- **Backend:** JWT configuration in `config.ts` with placeholder secret
|
||||
- **Frontend:** No authentication UI implemented (routes are placeholder)
|
||||
|
||||
**Security:**
|
||||
- **Backend:** Helmet.js for security headers, CORS configuration
|
||||
- **Frontend:** Service Worker for PWA capabilities (production only)
|
||||
|
||||
---
|
||||
|
||||
*Architecture analysis: 2026-03-13*
|
||||
90
.planning/codebase/STACK.md
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
# Technology Stack
|
||||
|
||||
**Analysis Date:** 2026-03-13
|
||||
|
||||
## Languages
|
||||
|
||||
**Primary:**
|
||||
- TypeScript 5.2.2 - Both backend (`backend/`) and frontend (`frontend/`)
|
||||
- JavaScript (via transpilation) - Runtime execution
|
||||
|
||||
**Secondary:**
|
||||
- HTML/CSS - Static promo site (`promo-site/`)
|
||||
|
||||
## Runtime
|
||||
|
||||
**Environment:**
|
||||
- Node.js (runtime for backend)
|
||||
- Browser (runtime for frontend)
|
||||
|
||||
**Package Manager:**
|
||||
- npm (lockfiles: `package-lock.json` in both `backend/` and `frontend/`)
|
||||
- Lockfile: Present
|
||||
|
||||
## Frameworks
|
||||
|
||||
**Core:**
|
||||
- Express 4.18.2 - Backend API server (`backend/package.json`)
|
||||
- React 18.2.0 - Frontend UI library (`frontend/package.json`)
|
||||
|
||||
**Testing:**
|
||||
- Jest 29.6.2 - Unit testing for backend (`backend/package.json`)
|
||||
- ts-jest - TypeScript support for Jest
|
||||
|
||||
**Build/Dev:**
|
||||
- Vite 4.4.9 - Frontend build tool and dev server (`frontend/package.json`)
|
||||
- TypeScript 5.2.2 - Type checking and compilation for both layers
|
||||
- ts-node-dev - Development server for backend (`backend/package.json`)
|
||||
|
||||
## Key Dependencies
|
||||
|
||||
**Critical (Backend):**
|
||||
- `express` 4.18.2 - HTTP server framework
|
||||
- `mongoose` 7.5.0 - MongoDB ODM (configured, partially implemented)
|
||||
- `jsonwebtoken` 9.0.2 - Authentication tokens
|
||||
- `bcryptjs` 2.4.3 - Password hashing
|
||||
- `zod` 3.22.2 - Schema validation (available, not fully utilized)
|
||||
- `socket.io` 4.7.2 - Real-time communication (installed, usage unclear)
|
||||
|
||||
**Critical (Frontend):**
|
||||
- `react` 18.2.0 - UI library
|
||||
- `react-router-dom` 6.15.0 - Client-side routing
|
||||
- `tailwindcss` 3.3.3 - Utility-first CSS framework
|
||||
- `next-themes` 0.2.1 - Dark mode handling
|
||||
- `lucide-react` 0.263.1 - Icon library
|
||||
- `vite` 4.4.9 - Build tool
|
||||
|
||||
**Infrastructure:**
|
||||
- `cors` 2.8.5 - Backend CORS handling
|
||||
- `helmet` 7.0.0 - Security headers
|
||||
- `multer` 1.4.5-lts.1 - File upload handling
|
||||
|
||||
## Configuration
|
||||
|
||||
**Environment:**
|
||||
- **Backend:** `.env` file in root (not shown in structure, referenced in code)
|
||||
- Configured in `backend/src/config.ts`
|
||||
- Key configs: `PORT`, `NODE_ENV`, `CORS_ORIGIN`, `MONGODB_URI`, `JWT_SECRET`, `UPLOADS_DIR`
|
||||
- **Frontend:** `.env` file in `frontend/` (not read due to security rules, likely Vite env vars)
|
||||
|
||||
**Build:**
|
||||
- **Backend:** `backend/tsconfig.json` - TypeScript configuration
|
||||
- **Frontend:**
|
||||
- `frontend/vite.config.ts` - Vite build configuration with path aliases
|
||||
- `frontend/tailwind.config.ts` - TailwindCSS theme configuration
|
||||
- `frontend/postcss.config.js` - PostCSS processing
|
||||
|
||||
## Platform Requirements
|
||||
|
||||
**Development:**
|
||||
- Node.js environment (backend)
|
||||
- Modern browser with ES6+ support (frontend)
|
||||
|
||||
**Production:**
|
||||
- Node.js server for backend API
|
||||
- Static file hosting for frontend (Vite build output)
|
||||
- Static file hosting for promo site
|
||||
|
||||
---
|
||||
|
||||
*Stack analysis: 2026-03-13*
|
||||
|
|
@ -9,6 +9,7 @@
|
|||
"plan_check": true,
|
||||
"verifier": true,
|
||||
"nyquist_validation": true,
|
||||
"auto_advance": true
|
||||
"auto_advance": true,
|
||||
"_auto_chain_active": true
|
||||
}
|
||||
}
|
||||
}
|
||||
18
.planning/phases/08-paginas-esenciales/08-01-PLAN.md
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
# 08-01: Favoritos + useFavorites
|
||||
|
||||
## Objective
|
||||
Crear página Favoritos.tsx y hook useFavorites para persistir y leer favoritos del usuario.
|
||||
|
||||
## Requirements
|
||||
- Hook useFavorites con localStorage/IndexedDB
|
||||
- Página Favoritos.tsx con listado de protocolos/guías guardados
|
||||
- Poder agregar/quitar favoritos desde cualquier página
|
||||
|
||||
## Deliverable
|
||||
- `/frontend/src/hooks/useFavorites.ts`
|
||||
- `/frontend/src/pages/Favoritos.tsx`
|
||||
|
||||
## Test Criteria
|
||||
- [ ] Puedo guardar un protocolo en favoritos
|
||||
- [ ] Puedo ver la lista de favoritos
|
||||
- [ ] Persisten al recargar la página
|
||||
18
.planning/phases/08-paginas-esenciales/08-02-PLAN.md
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
# 08-02: Historial + useHistory
|
||||
|
||||
## Objective
|
||||
Crear página Historial.tsx y hook useHistory para registrar y mostrar últimas consultas.
|
||||
|
||||
## Requirements
|
||||
- Hook useHistory con localStorage/IndexedDB
|
||||
- Página Historial.tsx con listado de últimos protocolos vistos
|
||||
- Timestamps para cada entrada
|
||||
|
||||
## Deliverable
|
||||
- `/frontend/src/hooks/useHistory.ts`
|
||||
- `/frontend/src/pages/Historial.tsx`
|
||||
|
||||
## Test Criteria
|
||||
- [ ] Al ver un protocolo, se registra en historial
|
||||
- [ ] Muestra los últimos 20 accesos
|
||||
- [ ] Persisten al recargar la página
|
||||
22
.planning/phases/08-paginas-esenciales/08-03-PLAN.md
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
# 08-03: Urgencias.tsx
|
||||
|
||||
## Objective
|
||||
Crear página de acceso rápido a protocolos críticos para situaciones de urgencia.
|
||||
|
||||
## Requirements
|
||||
- Página con accesos directos a protocolos más críticos:
|
||||
- PCR
|
||||
- OVACE
|
||||
- Anafilaxia
|
||||
- SCA
|
||||
- Ictus
|
||||
- Shock
|
||||
- Diseño optimizado para acceso rápido en emergencias
|
||||
|
||||
## Deliverable
|
||||
- `/frontend/src/pages/Urgencias.tsx`
|
||||
|
||||
## Test Criteria
|
||||
- [ ] Muestra los 6 protocolos críticos
|
||||
- [ ] Acceso rápido con un tap
|
||||
- [ ] Diseño visible en condiciones de estrés
|
||||
18
.planning/phases/08-paginas-esenciales/08-04-PLAN.md
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
# 08-04: Parto.tsx + protocolo YAML
|
||||
|
||||
## Objective
|
||||
Crear página Parto.tsx con el protocolo de parto extrahospitalario.
|
||||
|
||||
## Requirements
|
||||
- Protocolo YAML de parto emergencia
|
||||
- Página Parto.tsx que renderice el protocolo
|
||||
- Pasos para asistir parto en entorno prehospitalario
|
||||
|
||||
## Deliverable
|
||||
- `/frontend/public/protocols/emergencias/parto-emergencia.yaml` (ya existe)
|
||||
- `/frontend/src/pages/Parto.tsx`
|
||||
|
||||
## Test Criteria
|
||||
- [ ] Protocolo se renderiza correctamente
|
||||
- [ ] Todos los pasos visibles
|
||||
- [ ] Compatible con VisualProtocolRenderer
|
||||
17
.planning/phases/08-paginas-esenciales/08-05-PLAN.md
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
# 08-05: Avisos Legales
|
||||
|
||||
## Objective
|
||||
Crear páginas de Aviso Legal y Descargo de Responsabilidad médicas.
|
||||
|
||||
## Requirements
|
||||
- AvisoLegal.tsx con texto legal estándar
|
||||
- DescargoResponsabilidad.tsx con aviso de uso responsable
|
||||
- Links en pie de página
|
||||
|
||||
## Deliverable
|
||||
- `/frontend/src/pages/AvisoLegal.tsx`
|
||||
- `/frontend/src/pages/DescargoResponsabilidad.tsx`
|
||||
|
||||
## Test Criteria
|
||||
- [ ] Ambas páginas accesibles desde el menú
|
||||
- [ ] Contenido apropiado para app médica
|
||||
3
.vscode/settings.json
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"git.ignoreLimitWarning": true
|
||||
}
|
||||
23
README.md
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
# codigo0
|
||||
|
||||
Asistente avanzado de referencia médica para Técnicos de Emergencias Sanitarias (TES).
|
||||
|
||||
## 🚀 Características
|
||||
- **Protocolos Clínicos**: Visualización interactiva de guías de soporte vital.
|
||||
- **Herramientas Operativas**: Glasgow, Triage START, Superficie Quemada y Dosis Pediátricas.
|
||||
- **Manual del TES**: Guía técnica completa integrada para consulta rápida.
|
||||
- **PWA**: Soporte offline total.
|
||||
|
||||
## 🛠️ Stack Tecnológico
|
||||
- **Frontend**: React 18 + Vite + TailwindCSS
|
||||
- **Backend**: Node.js + Express + TypeScript (Clean Architecture)
|
||||
- **Base de Datos**: MongoDB (Dockerized)
|
||||
|
||||
## 📄 Documentación y Memoria
|
||||
Para mantener la rama principal limpia y centrada en el código, el seguimiento del proyecto se gestiona en dos ubicaciones:
|
||||
|
||||
1. **Git (Rama `docs-memoria`)**: Contiene `PROGRESS.md` y `CONTEXT.md` actualizados con el historial técnico.
|
||||
2. **Obsidian (Bóveda `99-agentes`)**: Contiene el Plan Maestro, Auditoría de Medios y Contexto Global.
|
||||
|
||||
---
|
||||
*codigo0 — 0 Errores. 0 Dudas.*
|
||||
30
backend/Dockerfile
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
# Etapa 1: Construcción del backend
|
||||
FROM node:18-alpine AS build-stage
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Instalar dependencias
|
||||
COPY package*.json ./
|
||||
RUN npm install
|
||||
|
||||
# Copiar el resto de archivos y construir
|
||||
COPY . .
|
||||
RUN npm run build
|
||||
|
||||
# Etapa 2: Imagen para ejecución
|
||||
FROM node:18-alpine
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Copiar solo dependencias de producción y archivos construidos
|
||||
COPY package*.json ./
|
||||
RUN npm install --production
|
||||
|
||||
COPY --from=build-stage /app/dist/ ./dist/
|
||||
|
||||
# Exponer el puerto por defecto (3000)
|
||||
EXPOSE 3000
|
||||
|
||||
ENV NODE_ENV=production
|
||||
|
||||
CMD ["node", "dist/index.js"]
|
||||
6075
backend/package-lock.json
generated
Normal file
40
backend/package.json
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
{
|
||||
"name": "codigo0-backend",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "ts-node-dev --respawn --transpile-only src/index.ts",
|
||||
"build": "tsc",
|
||||
"start": "node dist/index.js",
|
||||
"test": "jest",
|
||||
"migrate": "ts-node src/migrateYamlToMongo.ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"bcryptjs": "^2.4.3",
|
||||
"cors": "^2.8.5",
|
||||
"dotenv": "^16.3.1",
|
||||
"express": "^4.18.2",
|
||||
"helmet": "^7.0.0",
|
||||
"js-yaml": "^4.1.1",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"mongoose": "^7.5.0",
|
||||
"multer": "^1.4.5-lts.1",
|
||||
"socket.io": "^4.7.2",
|
||||
"zod": "^3.22.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/bcryptjs": "^2.4.2",
|
||||
"@types/cors": "^2.8.13",
|
||||
"@types/express": "^4.17.17",
|
||||
"@types/jest": "^29.5.5",
|
||||
"@types/js-yaml": "^4.0.9",
|
||||
"@types/jsonwebtoken": "^9.0.2",
|
||||
"@types/multer": "^1.4.7",
|
||||
"@types/node": "^20.6.3",
|
||||
"@types/socket.io": "^3.0.2",
|
||||
"jest": "^29.6.2",
|
||||
"ts-jest": "^29.1.1",
|
||||
"ts-node-dev": "^2.0.0",
|
||||
"typescript": "^5.2.2"
|
||||
}
|
||||
}
|
||||
60
backend/src/app.ts
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
import express, { Request, Response, NextFunction } from 'express';
|
||||
import cors from 'cors';
|
||||
import helmet from 'helmet';
|
||||
import dotenv from 'dotenv';
|
||||
import authRoutes from './routes/auth';
|
||||
import protocolRoutes from './infrastructure/http/routes/protocol.routes';
|
||||
|
||||
// Load environment variables
|
||||
dotenv.config();
|
||||
|
||||
export function createApp() {
|
||||
const app = express();
|
||||
|
||||
// Security middleware
|
||||
app.use(helmet());
|
||||
|
||||
// CORS configuration
|
||||
app.use(cors({
|
||||
origin: process.env.CORS_ORIGIN || '*',
|
||||
methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
|
||||
allowedHeaders: ['Content-Type', 'Authorization']
|
||||
}));
|
||||
|
||||
// Body parsing
|
||||
app.use(express.json({ limit: '10mb' }));
|
||||
app.use(express.urlencoded({ extended: true, limit: '10mb' }));
|
||||
|
||||
// Health check endpoint
|
||||
app.get('/health', (_req, res) => {
|
||||
res.json({
|
||||
status: 'ok',
|
||||
timestamp: new Date().toISOString(),
|
||||
service: 'codigo0-backend',
|
||||
version: '0.1.0'
|
||||
});
|
||||
});
|
||||
|
||||
// API routes
|
||||
app.use('/api/auth', authRoutes);
|
||||
app.use('/api/content', protocolRoutes);
|
||||
|
||||
// 404 handler
|
||||
app.use((_req, res) => {
|
||||
res.status(404).json({
|
||||
error: 'Not Found',
|
||||
message: 'The requested resource was not found'
|
||||
});
|
||||
});
|
||||
|
||||
// Error handler
|
||||
app.use((err: any, _req: Request, res: Response, _next: NextFunction) => {
|
||||
console.error(err.stack);
|
||||
res.status(500).json({
|
||||
error: 'Internal Server Error',
|
||||
message: process.env.NODE_ENV === 'development' ? err.message : 'Something went wrong'
|
||||
});
|
||||
});
|
||||
|
||||
return app;
|
||||
}
|
||||
10
backend/src/application/usecases/GetAllProtocolsUseCase.ts
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
import { IProtocolRepository } from '../../domain/repositories/IProtocolRepository';
|
||||
import { TelephoneProtocol } from '../../domain/entities/TelephoneProtocol';
|
||||
|
||||
export class GetAllProtocolsUseCase {
|
||||
constructor(private protocolRepository: IProtocolRepository) {}
|
||||
|
||||
async execute(): Promise<TelephoneProtocol[]> {
|
||||
return this.protocolRepository.findAll();
|
||||
}
|
||||
}
|
||||
10
backend/src/application/usecases/GetProtocolByIdUseCase.ts
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
import { IProtocolRepository } from '../../domain/repositories/IProtocolRepository';
|
||||
import { TelephoneProtocol } from '../../domain/entities/TelephoneProtocol';
|
||||
|
||||
export class GetProtocolByIdUseCase {
|
||||
constructor(private protocolRepository: IProtocolRepository) {}
|
||||
|
||||
async execute(id: string): Promise<TelephoneProtocol | null> {
|
||||
return this.protocolRepository.findById(id);
|
||||
}
|
||||
}
|
||||
17
backend/src/config.ts
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
import dotenv from 'dotenv';
|
||||
|
||||
dotenv.config();
|
||||
|
||||
export const config = {
|
||||
port: parseInt(process.env.PORT || '3000', 10),
|
||||
nodeEnv: process.env.NODE_ENV || 'development',
|
||||
corsOrigin: process.env.CORS_ORIGIN || '*',
|
||||
// Database configuration (to be implemented)
|
||||
dbUri: process.env.MONGODB_URI || 'mongodb://localhost:27017/codigo0',
|
||||
// JWT configuration
|
||||
jwtSecret: process.env.JWT_SECRET || 'your-secret-key-change-in-production',
|
||||
jwtExpiresIn: process.env.JWT_EXPIRES_IN || '7d',
|
||||
// File upload configuration
|
||||
uploadsDir: process.env.UPLOADS_DIR || './uploads',
|
||||
maxFileSize: parseInt(process.env.MAX_FILE_SIZE || '10485760', 10), // 10MB
|
||||
};
|
||||
46
backend/src/domain/entities/ClinicalProtocol.ts
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
export interface Drug {
|
||||
nombre: string;
|
||||
filas: { label: string; valor: string }[];
|
||||
formula?: string;
|
||||
}
|
||||
|
||||
export interface ProtocolStep {
|
||||
tipo: 'checklist' | 'decision' | 'tecnica' | 'info';
|
||||
titulo: string;
|
||||
descripcion?: string;
|
||||
items?: string[];
|
||||
urgencia?: 'critica';
|
||||
badge?: string;
|
||||
parametros?: { label: string; valor: string; destacado?: boolean }[];
|
||||
si?: string;
|
||||
no?: string;
|
||||
imagen?: string;
|
||||
video?: string;
|
||||
}
|
||||
|
||||
export interface ProtocolPhase extends Omit<ProtocolStep, 'titulo'> {
|
||||
id: string | number;
|
||||
nombre: string;
|
||||
icono?: string;
|
||||
pregunta?: string;
|
||||
ramas?: { condicion: string; destino: string | number }[];
|
||||
}
|
||||
|
||||
export interface ClinicalProtocol {
|
||||
id: string; // Slug único
|
||||
titulo: string;
|
||||
subtitulo?: string;
|
||||
categoria: string;
|
||||
urgencia: 'critica' | 'alta' | 'media' | 'baja';
|
||||
version: string;
|
||||
descripcion?: string;
|
||||
fuente?: string;
|
||||
actualizado?: string;
|
||||
alertas?: { tipo: 'warning' | 'danger' | 'info'; texto: string }[];
|
||||
grupos_edad?: { id: string; label: string; sublabel?: string }[];
|
||||
parametros_por_grupo?: Record<string, Record<string, string>>;
|
||||
pasos?: ProtocolStep[];
|
||||
fases?: ProtocolPhase[];
|
||||
farmacos?: Drug[];
|
||||
equipamiento?: string[];
|
||||
}
|
||||
24
backend/src/domain/entities/TelephoneProtocol.ts
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
export type ProtocolCategory = 'rcp' | 'desa' | 'ovace' | 'sca' | 'ictus' | 'comunicacion';
|
||||
export type AgeGroup = 'adulto' | 'pediatrico' | 'lactante' | 'todos';
|
||||
|
||||
export interface ProtocolStep {
|
||||
id: string;
|
||||
order: number;
|
||||
instruction: string;
|
||||
verification?: string;
|
||||
notes?: string;
|
||||
critical?: boolean;
|
||||
}
|
||||
|
||||
export interface TelephoneProtocol {
|
||||
id: string;
|
||||
title: string;
|
||||
shortTitle: string;
|
||||
category: ProtocolCategory;
|
||||
ageGroup: AgeGroup;
|
||||
description: string;
|
||||
initialAssessment: string[];
|
||||
steps: ProtocolStep[];
|
||||
importantNotes?: string[];
|
||||
source?: string;
|
||||
}
|
||||
8
backend/src/domain/repositories/IProtocolRepository.ts
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
import { TelephoneProtocol, ProtocolCategory, AgeGroup } from '../entities/TelephoneProtocol';
|
||||
|
||||
export interface IProtocolRepository {
|
||||
findAll(): Promise<TelephoneProtocol[]>;
|
||||
findById(id: string): Promise<TelephoneProtocol | null>;
|
||||
findByCategory(category: ProtocolCategory): Promise<TelephoneProtocol[]>;
|
||||
findByAgeGroup(ageGroup: AgeGroup): Promise<TelephoneProtocol[]>;
|
||||
}
|
||||
29
backend/src/index.ts
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
import { createApp } from './app';
|
||||
import { config } from './config';
|
||||
|
||||
const app = createApp();
|
||||
const PORT = config.port || 3000;
|
||||
|
||||
// Start the server
|
||||
const server = app.listen(PORT, () => {
|
||||
console.log(`Server running on port ${PORT}`);
|
||||
});
|
||||
|
||||
// Graceful shutdown
|
||||
process.on('SIGINT', () => {
|
||||
console.log('SIGINT received: shutting down gracefully');
|
||||
server.close(() => {
|
||||
console.log('Closed out remaining connections.');
|
||||
process.exit(0);
|
||||
});
|
||||
});
|
||||
|
||||
process.on('SIGTERM', () => {
|
||||
console.log('SIGTERM received: shutting down gracefully');
|
||||
server.close(() => {
|
||||
console.log('Closed out remaining connections.');
|
||||
process.exit(0);
|
||||
});
|
||||
});
|
||||
|
||||
export default server;
|
||||
79
backend/src/infrastructure/database/models/ProtocolModel.ts
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
import mongoose, { Schema, Document } from 'mongoose';
|
||||
import { ClinicalProtocol } from '../../domain/entities/ClinicalProtocol';
|
||||
|
||||
const ProtocolStepSchema = new Schema({
|
||||
tipo: { type: String, enum: ['checklist', 'decision', 'tecnica', 'info'], required: true },
|
||||
titulo: { type: String, required: true },
|
||||
descripcion: String,
|
||||
items: [String],
|
||||
urgencia: { type: String, enum: ['critica'] },
|
||||
badge: String,
|
||||
parametros: [{
|
||||
label: String,
|
||||
valor: String,
|
||||
destacado: Boolean
|
||||
}],
|
||||
si: String,
|
||||
no: String,
|
||||
imagen: String,
|
||||
video: String
|
||||
}, { _id: false });
|
||||
|
||||
const ProtocolPhaseSchema = new Schema({
|
||||
id: Schema.Types.Mixed,
|
||||
nombre: { type: String, required: true },
|
||||
icono: String,
|
||||
pregunta: String,
|
||||
ramas: [{
|
||||
condicion: String,
|
||||
destino: Schema.Types.Mixed
|
||||
}],
|
||||
// Reutilizamos campos de step por consistencia
|
||||
tipo: String,
|
||||
descripcion: String,
|
||||
items: [String],
|
||||
urgencia: String,
|
||||
badge: String,
|
||||
parametros: [{ label: String, valor: String, destacado: Boolean }],
|
||||
imagen: String,
|
||||
video: String
|
||||
}, { _id: false });
|
||||
|
||||
const DrugSchema = new Schema({
|
||||
nombre: { type: String, required: true },
|
||||
filas: [{ label: String, valor: String }],
|
||||
formula: String
|
||||
}, { _id: false });
|
||||
|
||||
const ClinicalProtocolSchema = new Schema({
|
||||
id: { type: String, required: true, unique: true },
|
||||
titulo: { type: String, required: true },
|
||||
subtitulo: String,
|
||||
categoria: { type: String, required: true },
|
||||
urgencia: { type: String, enum: ['critica', 'alta', 'media', 'baja'], required: true },
|
||||
version: { type: String, required: true },
|
||||
descripcion: String,
|
||||
fuente: String,
|
||||
actualizado: String,
|
||||
alertas: [{
|
||||
tipo: { type: String, enum: ['warning', 'danger', 'info'] },
|
||||
texto: String
|
||||
}],
|
||||
grupos_edad: [{
|
||||
id: String,
|
||||
label: String,
|
||||
sublabel: String
|
||||
}],
|
||||
parametros_por_grupo: Schema.Types.Map,
|
||||
pasos: [ProtocolStepSchema],
|
||||
fases: [ProtocolPhaseSchema],
|
||||
farmacos: [DrugSchema],
|
||||
equipamiento: [String]
|
||||
}, {
|
||||
timestamps: true,
|
||||
collection: 'protocols'
|
||||
});
|
||||
|
||||
export interface ProtocolDocument extends ClinicalProtocol, Document {}
|
||||
|
||||
export const ProtocolModel = mongoose.model<ProtocolDocument>('Protocol', ClinicalProtocolSchema);
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
import { Request, Response } from 'express';
|
||||
import { GetAllProtocolsUseCase } from '../../../application/usecases/GetAllProtocolsUseCase';
|
||||
import { GetProtocolByIdUseCase } from '../../../application/usecases/GetProtocolByIdUseCase';
|
||||
|
||||
export class ProtocolController {
|
||||
constructor(
|
||||
private getAllProtocolsUseCase: GetAllProtocolsUseCase,
|
||||
private getProtocolByIdUseCase: GetProtocolByIdUseCase
|
||||
) {}
|
||||
|
||||
getAll = async (_req: Request, res: Response): Promise<void> => {
|
||||
try {
|
||||
const protocols = await this.getAllProtocolsUseCase.execute();
|
||||
res.json({
|
||||
message: 'Telephone protocols list',
|
||||
data: protocols
|
||||
});
|
||||
} catch (error) {
|
||||
res.status(500).json({ message: 'Internal server error' });
|
||||
}
|
||||
};
|
||||
|
||||
getById = async (req: Request, res: Response): Promise<void> => {
|
||||
try {
|
||||
const protocol = await this.getProtocolByIdUseCase.execute(req.params.id);
|
||||
if (protocol) {
|
||||
res.json({
|
||||
message: 'Protocol found',
|
||||
data: protocol
|
||||
});
|
||||
} else {
|
||||
res.status(404).json({
|
||||
message: 'Protocol not found',
|
||||
id: req.params.id
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
res.status(500).json({ message: 'Internal server error' });
|
||||
}
|
||||
};
|
||||
}
|
||||
34
backend/src/infrastructure/http/routes/protocol.routes.ts
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
import { Router } from 'express';
|
||||
import { ProtocolController } from '../controllers/ProtocolController';
|
||||
import { GetAllProtocolsUseCase } from '../../../application/usecases/GetAllProtocolsUseCase';
|
||||
import { GetProtocolByIdUseCase } from '../../../application/usecases/GetProtocolByIdUseCase';
|
||||
import { StaticProtocolRepository } from '../../persistence/StaticProtocolRepository';
|
||||
|
||||
// manual dependency injection for routes
|
||||
const repository = new StaticProtocolRepository();
|
||||
const getAllUseCase = new GetAllProtocolsUseCase(repository);
|
||||
const getByIdUseCase = new GetProtocolByIdUseCase(repository);
|
||||
const controller = new ProtocolController(getAllUseCase, getByIdUseCase);
|
||||
|
||||
const router = Router();
|
||||
|
||||
router.get('/', controller.getAll);
|
||||
router.get('/:id', controller.getById);
|
||||
|
||||
// Placeholders for future endpoints matching the original content.ts routes
|
||||
router.post('/', (_req, res) => {
|
||||
res.status(201).json({
|
||||
message: 'Create content endpoint - to be implemented',
|
||||
id: 'new-content-id'
|
||||
});
|
||||
});
|
||||
|
||||
router.put('/:id', (req, res) => {
|
||||
res.json({ message: `Update content ${req.params.id} - to be implemented` });
|
||||
});
|
||||
|
||||
router.delete('/:id', (req, res) => {
|
||||
res.json({ message: `Delete content ${req.params.id} - to be implemented` });
|
||||
});
|
||||
|
||||
export default router;
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
import { IProtocolRepository } from '../../domain/repositories/IProtocolRepository';
|
||||
import { TelephoneProtocol, ProtocolCategory, AgeGroup } from '../../domain/entities/TelephoneProtocol';
|
||||
import { telephoneProtocols } from '../../services/telephone-protocols';
|
||||
|
||||
export class StaticProtocolRepository implements IProtocolRepository {
|
||||
async findAll(): Promise<TelephoneProtocol[]> {
|
||||
return telephoneProtocols;
|
||||
}
|
||||
|
||||
async findById(id: string): Promise<TelephoneProtocol | null> {
|
||||
const protocol = telephoneProtocols.find(p => p.id === id);
|
||||
return protocol || null;
|
||||
}
|
||||
|
||||
async findByCategory(category: ProtocolCategory): Promise<TelephoneProtocol[]> {
|
||||
return telephoneProtocols.filter(p => p.category === category);
|
||||
}
|
||||
|
||||
async findByAgeGroup(ageGroup: AgeGroup): Promise<TelephoneProtocol[]> {
|
||||
return telephoneProtocols.filter(p => p.ageGroup === ageGroup || p.ageGroup === 'todos');
|
||||
}
|
||||
}
|
||||
59
backend/src/migrateYamlToMongo.ts
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
import mongoose from 'mongoose';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import yaml from 'js-yaml';
|
||||
import dotenv from 'dotenv';
|
||||
import { ProtocolModel } from './infrastructure/database/models/ProtocolModel';
|
||||
import { ClinicalProtocol } from './domain/entities/ClinicalProtocol';
|
||||
|
||||
dotenv.config();
|
||||
|
||||
const MONGODB_URI = process.env.MONGODB_URI || 'mongodb://localhost:27017/codigo0';
|
||||
const PROTOCOLS_DIR = path.join(__dirname, '../../frontend/public/protocols');
|
||||
|
||||
async function migrate() {
|
||||
try {
|
||||
console.log('🚀 Iniciando migración de protocolos YAML a MongoDB...');
|
||||
await mongoose.connect(MONGODB_URI);
|
||||
console.log('✅ Conectado a MongoDB');
|
||||
|
||||
const folders = fs.readdirSync(PROTOCOLS_DIR);
|
||||
let totalMigrated = 0;
|
||||
|
||||
for (const folder of folders) {
|
||||
const folderPath = path.join(PROTOCOLS_DIR, folder);
|
||||
if (!fs.statSync(folderPath).isDirectory()) continue;
|
||||
|
||||
const files = fs.readdirSync(folderPath).filter(f => f.endsWith('.yaml'));
|
||||
|
||||
for (const file of files) {
|
||||
const filePath = path.join(folderPath, file);
|
||||
const content = fs.readFileSync(filePath, 'utf8');
|
||||
const data = yaml.load(content) as ClinicalProtocol;
|
||||
|
||||
if (!data.id) {
|
||||
data.id = file.replace('.yaml', '');
|
||||
}
|
||||
|
||||
// Upsert
|
||||
await ProtocolModel.findOneAndUpdate(
|
||||
{ id: data.id },
|
||||
data,
|
||||
{ upsert: true, new: true, setDefaultsOnInsert: true }
|
||||
);
|
||||
|
||||
console.log(` 📄 Migrado: [${folder}] ${data.titulo} (${data.id})`);
|
||||
totalMigrated++;
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`\n🎉 Migración completada. Total: ${totalMigrated} protocolos.`);
|
||||
} catch (error) {
|
||||
console.error('❌ Error durante la migración:', error);
|
||||
} finally {
|
||||
await mongoose.disconnect();
|
||||
process.exit();
|
||||
}
|
||||
}
|
||||
|
||||
migrate();
|
||||
36
backend/src/routes/auth.ts
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
import { Router } from 'express';
|
||||
|
||||
const router = Router();
|
||||
|
||||
/**
|
||||
* POST /api/auth/login
|
||||
* Login endpoint (placeholder)
|
||||
*/
|
||||
router.post('/login', (_req, res) => {
|
||||
res.json({
|
||||
message: 'Login endpoint - to be implemented',
|
||||
// In a real app, this would validate credentials and return a JWT
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* POST /api/auth/logout
|
||||
* Logout endpoint (placeholder)
|
||||
*/
|
||||
router.post('/logout', (_req, res) => {
|
||||
res.json({
|
||||
message: 'Logout endpoint - to be implemented'
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* POST /api/auth/refresh
|
||||
* Refresh token endpoint (placeholder)
|
||||
*/
|
||||
router.post('/refresh', (_req, res) => {
|
||||
res.json({
|
||||
message: 'Refresh token endpoint - to be implemented'
|
||||
});
|
||||
});
|
||||
|
||||
export default router;
|
||||
67
backend/src/routes/content.ts
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
import { Router } from 'express';
|
||||
import { telephoneProtocols, getProtocolById } from '../services/telephone-protocols';
|
||||
|
||||
const router = Router();
|
||||
|
||||
/**
|
||||
* GET /api/content
|
||||
* Get all telephone protocols
|
||||
*/
|
||||
router.get('/', (_req, res) => {
|
||||
res.json({
|
||||
message: 'Telephone protocols list',
|
||||
data: telephoneProtocols
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* GET /api/content/:id
|
||||
* Get telephone protocol by ID
|
||||
*/
|
||||
router.get('/:id', (req, res) => {
|
||||
const protocol = getProtocolById(req.params.id);
|
||||
if (protocol) {
|
||||
res.json({
|
||||
message: 'Protocol found',
|
||||
data: protocol
|
||||
});
|
||||
} else {
|
||||
res.status(404).json({
|
||||
message: 'Protocol not found',
|
||||
id: req.params.id
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* POST /api/content
|
||||
* Create new content (placeholder)
|
||||
*/
|
||||
router.post('/', (_req, res) => {
|
||||
res.status(201).json({
|
||||
message: 'Create content endpoint - to be implemented',
|
||||
id: 'new-content-id'
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* PUT /api/content/:id
|
||||
* Update content (placeholder)
|
||||
*/
|
||||
router.put('/:id', (_req, res) => {
|
||||
res.json({
|
||||
message: `Update content ${_req.params.id} - to be implemented`
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* DELETE /api/content/:id
|
||||
* Delete content (placeholder)
|
||||
*/
|
||||
router.delete('/:id', (_req, res) => {
|
||||
res.json({
|
||||
message: `Delete content ${_req.params.id} - to be implemented`
|
||||
});
|
||||
});
|
||||
|
||||
export default router;
|
||||
18
backend/src/routes/health.ts
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
import { Router } from 'express';
|
||||
|
||||
const router = Router();
|
||||
|
||||
/**
|
||||
* GET /api/health
|
||||
* Health check básico
|
||||
*/
|
||||
router.get('/', (_req, res) => {
|
||||
res.json({
|
||||
status: 'ok',
|
||||
timestamp: new Date().toISOString(),
|
||||
service: 'codigo0-backend',
|
||||
version: '0.1.0'
|
||||
});
|
||||
});
|
||||
|
||||
export default router;
|
||||
671
backend/src/services/telephone-protocols.ts
Executable file
|
|
@ -0,0 +1,671 @@
|
|||
/**
|
||||
* Protocolos Transtelefónicos - Manual TES Digital
|
||||
*
|
||||
* Este módulo contiene protocolos para guiar a testigos por teléfono
|
||||
* en situaciones de emergencia (RCP, DESA, OVACE, SCA, Ictus).
|
||||
*
|
||||
* Fuente: Manual TES Digital (Bloque 5)
|
||||
*/
|
||||
|
||||
export type ProtocolCategory = 'rcp' | 'desa' | 'ovace' | 'sca' | 'ictus' | 'comunicacion';
|
||||
export type AgeGroup = 'adulto' | 'pediatrico' | 'lactante' | 'todos';
|
||||
|
||||
export interface ProtocolStep {
|
||||
id: string;
|
||||
order: number;
|
||||
instruction: string; // Instrucción para leer al testigo
|
||||
verification?: string; // Pregunta para verificar que se está haciendo
|
||||
notes?: string; // Notas para el operador (no se leen al testigo)
|
||||
critical?: boolean; // Si es crítico, debe verificarse antes de continuar
|
||||
}
|
||||
|
||||
export interface TelephoneProtocol {
|
||||
id: string;
|
||||
title: string;
|
||||
shortTitle: string;
|
||||
category: ProtocolCategory;
|
||||
ageGroup: AgeGroup;
|
||||
description: string;
|
||||
initialAssessment: string[]; // Preguntas de evaluación inicial
|
||||
steps: ProtocolStep[];
|
||||
importantNotes?: string[]; // Notas importantes para el operador
|
||||
source?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Protocolo: RCP Transtelefónica - Adultos
|
||||
* Fuente: BLOQUE_04_1_RCP_ADULTOS.md (adaptado para guía telefónica)
|
||||
*/
|
||||
export const rcpTelephoneAdult: TelephoneProtocol = {
|
||||
id: 'rcp-telephone-adult',
|
||||
title: 'RCP Transtelefónica - Adultos',
|
||||
shortTitle: 'RCP Adulto',
|
||||
category: 'rcp',
|
||||
ageGroup: 'adulto',
|
||||
description: 'Guía paso a paso para que un testigo realice RCP en un adulto mientras llega la ambulancia.',
|
||||
initialAssessment: [
|
||||
'¿Está la persona consciente? ¿Responde si le habla o le toca?',
|
||||
'¿Está respirando normalmente? (No cuente boqueadas o respiraciones muy lentas)',
|
||||
'¿Es seguro acercarse? (No hay riesgo de tráfico, electricidad, fuego, violencia)',
|
||||
],
|
||||
steps: [
|
||||
{
|
||||
id: 'activar-112',
|
||||
order: 1,
|
||||
instruction: 'Primero, active el servicio de emergencias. ¿Ya ha llamado al 112?',
|
||||
verification: 'Confirme que ha llamado al 112 o que alguien más lo está haciendo',
|
||||
critical: true,
|
||||
notes: 'Si no ha llamado, guíe para que llame primero o que otra persona llame',
|
||||
},
|
||||
{
|
||||
id: 'colocar-superficie',
|
||||
order: 2,
|
||||
instruction: 'Coloque a la persona boca arriba sobre una superficie firme y plana, como el suelo.',
|
||||
verification: '¿Está la persona boca arriba sobre una superficie firme?',
|
||||
critical: true,
|
||||
},
|
||||
{
|
||||
id: 'abrir-via-aerea',
|
||||
order: 3,
|
||||
instruction: 'Incline suavemente la cabeza hacia atrás y levante la barbilla. Esto abre la vía aérea.',
|
||||
verification: '¿Puede ver que la cabeza está inclinada hacia atrás y la barbilla levantada?',
|
||||
notes: 'Si sospecha trauma cervical, indique que no mueva la cabeza',
|
||||
},
|
||||
{
|
||||
id: 'verificar-respiracion',
|
||||
order: 4,
|
||||
instruction: 'Acérquese y mire, escuche y sienta si respira normalmente. Hágalo durante 10 segundos máximo.',
|
||||
verification: '¿Respira normalmente? (No cuente boqueadas o respiraciones muy lentas)',
|
||||
critical: true,
|
||||
},
|
||||
{
|
||||
id: 'iniciar-compresiones',
|
||||
order: 5,
|
||||
instruction: 'Si NO respira normalmente, coloque el talón de una mano en el centro del pecho, entre los pezones. Coloque la otra mano encima y entrelace los dedos.',
|
||||
verification: '¿Tiene las manos colocadas correctamente en el centro del pecho?',
|
||||
critical: true,
|
||||
},
|
||||
{
|
||||
id: 'compresiones-tecnica',
|
||||
order: 6,
|
||||
instruction: 'Con los brazos rectos y los hombros sobre las manos, comprima el pecho hacia abajo unos 5-6 centímetros. Deje que el pecho vuelva completamente arriba después de cada compresión.',
|
||||
verification: '¿Está comprimiendo hacia abajo y dejando que el pecho vuelva completamente arriba?',
|
||||
critical: true,
|
||||
notes: 'La profundidad y el recoil completo son críticos',
|
||||
},
|
||||
{
|
||||
id: 'ritmo-compresiones',
|
||||
order: 7,
|
||||
instruction: 'Haga las compresiones a un ritmo rápido y constante, aproximadamente 100-120 por minuto. Puede contar en voz alta: "uno, dos, tres..." hasta "treinta".',
|
||||
verification: '¿Está haciendo las compresiones a un ritmo rápido y constante?',
|
||||
critical: true,
|
||||
notes: 'El ritmo es crítico: demasiado lento o rápido reduce la efectividad',
|
||||
},
|
||||
{
|
||||
id: 'ventilaciones',
|
||||
order: 8,
|
||||
instruction: 'Después de 30 compresiones, incline la cabeza hacia atrás, levante la barbilla, pellizque la nariz y dé 2 respiraciones de rescate. Cada respiración debe durar 1 segundo y ver que el pecho se eleva.',
|
||||
verification: '¿Está dando las respiraciones y ve que el pecho se eleva?',
|
||||
notes: 'Si no puede o no quiere dar respiraciones, continúe solo con compresiones',
|
||||
},
|
||||
{
|
||||
id: 'continuar-ciclos',
|
||||
order: 9,
|
||||
instruction: 'Continúe con ciclos de 30 compresiones seguidas de 2 respiraciones. No pare hasta que llegue la ambulancia o la persona empiece a respirar normalmente.',
|
||||
verification: '¿Está continuando con los ciclos sin parar?',
|
||||
critical: true,
|
||||
},
|
||||
{
|
||||
id: 'cambio-reanimador',
|
||||
order: 10,
|
||||
instruction: 'Si hay otra persona disponible, pueden turnarse cada 2 minutos para evitar el cansancio. El cambio debe ser rápido, en menos de 5 segundos.',
|
||||
verification: '¿Hay alguien más que pueda ayudar?',
|
||||
notes: 'Solo si hay más personas disponibles',
|
||||
},
|
||||
],
|
||||
importantNotes: [
|
||||
'Si el testigo no puede o no quiere dar respiraciones, es mejor que continúe solo con compresiones que parar',
|
||||
'Las compresiones continuas sin respiraciones son mejores que no hacer nada',
|
||||
'No pare para buscar pulso: continúe hasta que llegue la ambulancia o la persona respire normalmente',
|
||||
'Si la persona empieza a respirar normalmente o se mueve, pare las compresiones y colóquela de lado',
|
||||
],
|
||||
source: 'BLOQUE_04_1_RCP_ADULTOS.md',
|
||||
};
|
||||
|
||||
/**
|
||||
* Protocolo: RCP Transtelefónica - Pediatría
|
||||
* Fuente: BLOQUE_04_2_RCP_PEDIATRIA.md (adaptado para guía telefónica)
|
||||
*/
|
||||
export const rcpTelephonePediatric: TelephoneProtocol = {
|
||||
id: 'rcp-telephone-pediatric',
|
||||
title: 'RCP Transtelefónica - Pediatría',
|
||||
shortTitle: 'RCP Pediátrico',
|
||||
category: 'rcp',
|
||||
ageGroup: 'pediatrico',
|
||||
description: 'Guía paso a paso para que un testigo realice RCP en un niño (1 año hasta pubertad) mientras llega la ambulancia.',
|
||||
initialAssessment: [
|
||||
'¿Está el niño consciente? ¿Responde si le habla o le toca?',
|
||||
'¿Está respirando normalmente? (No cuente boqueadas o respiraciones muy lentas)',
|
||||
'¿Es seguro acercarse?',
|
||||
'¿Cuántos años tiene el niño aproximadamente?',
|
||||
],
|
||||
steps: [
|
||||
{
|
||||
id: 'activar-112-ped',
|
||||
order: 1,
|
||||
instruction: 'Primero, active el servicio de emergencias. ¿Ya ha llamado al 112?',
|
||||
verification: 'Confirme que ha llamado al 112',
|
||||
critical: true,
|
||||
notes: 'Si está solo y el colapso no fue presenciado, puede hacer 2 minutos de RCP primero',
|
||||
},
|
||||
{
|
||||
id: 'colocar-superficie-ped',
|
||||
order: 2,
|
||||
instruction: 'Coloque al niño boca arriba sobre una superficie firme y plana. Si es pequeño, puede usar una mesa si es más firme que el suelo.',
|
||||
verification: '¿Está el niño boca arriba sobre una superficie firme?',
|
||||
critical: true,
|
||||
},
|
||||
{
|
||||
id: 'abrir-via-aerea-ped',
|
||||
order: 3,
|
||||
instruction: 'Incline suavemente la cabeza hacia atrás en posición neutra y levante la barbilla. Esto abre la vía aérea.',
|
||||
verification: '¿Puede ver que la cabeza está en posición neutra y la barbilla levantada?',
|
||||
},
|
||||
{
|
||||
id: 'verificar-respiracion-ped',
|
||||
order: 4,
|
||||
instruction: 'Acérquese y mire, escuche y sienta si respira normalmente. Hágalo durante 10 segundos máximo.',
|
||||
verification: '¿Respira normalmente?',
|
||||
critical: true,
|
||||
},
|
||||
{
|
||||
id: 'iniciar-compresiones-ped',
|
||||
order: 5,
|
||||
instruction: 'Si NO respira normalmente, coloque el talón de una mano (o dos manos si el niño es grande) en el centro del pecho, entre los pezones.',
|
||||
verification: '¿Tiene la(s) mano(s) colocada(s) correctamente en el centro del pecho?',
|
||||
critical: true,
|
||||
notes: 'Una mano para niños pequeños, dos manos para niños grandes/púberes',
|
||||
},
|
||||
{
|
||||
id: 'compresiones-tecnica-ped',
|
||||
order: 6,
|
||||
instruction: 'Comprima el pecho hacia abajo aproximadamente un tercio de la profundidad del pecho (unos 5 cm en la mayoría de niños). Deje que el pecho vuelva completamente arriba después de cada compresión.',
|
||||
verification: '¿Está comprimiendo hacia abajo y dejando que el pecho vuelva completamente arriba?',
|
||||
critical: true,
|
||||
},
|
||||
{
|
||||
id: 'ritmo-compresiones-ped',
|
||||
order: 7,
|
||||
instruction: 'Haga las compresiones a un ritmo rápido y constante, aproximadamente 100-120 por minuto. Cuente en voz alta: "uno, dos, tres..." hasta "quince" (si hay dos personas) o "treinta" (si está solo).',
|
||||
verification: '¿Está haciendo las compresiones a un ritmo rápido y constante?',
|
||||
critical: true,
|
||||
},
|
||||
{
|
||||
id: 'ventilaciones-ped',
|
||||
order: 8,
|
||||
instruction: 'Después de las compresiones (15 si hay dos personas, 30 si está solo), incline la cabeza, levante la barbilla, y dé 2 respiraciones de rescate cubriendo boca Y nariz del niño. Cada respiración debe durar 1 segundo y ver que el pecho se eleva.',
|
||||
verification: '¿Está dando las respiraciones cubriendo boca y nariz y ve que el pecho se eleva?',
|
||||
notes: 'En pediatría, la ventilación es tan importante como las compresiones',
|
||||
},
|
||||
{
|
||||
id: 'continuar-ciclos-ped',
|
||||
order: 9,
|
||||
instruction: 'Continúe con ciclos de compresiones y respiraciones. No pare hasta que llegue la ambulancia o el niño empiece a respirar normalmente.',
|
||||
verification: '¿Está continuando con los ciclos sin parar?',
|
||||
critical: true,
|
||||
},
|
||||
],
|
||||
importantNotes: [
|
||||
'En pediatría, la ventilación es tan crítica como las compresiones',
|
||||
'Si está solo y el colapso no fue presenciado, haga 2 minutos de RCP primero, luego active el 112',
|
||||
'Si el colapso fue presenciado súbito, active el 112 inmediatamente',
|
||||
'Las compresiones deben ser proporcionales al tamaño del niño',
|
||||
],
|
||||
source: 'BLOQUE_04_2_RCP_PEDIATRIA.md',
|
||||
};
|
||||
|
||||
/**
|
||||
* Protocolo: DESA Guiado por Teléfono
|
||||
* Fuente: BLOQUE_04_4_USO_DESA.md (adaptado para guía telefónica)
|
||||
*/
|
||||
export const desaTelephone: TelephoneProtocol = {
|
||||
id: 'desa-telephone',
|
||||
title: 'DESA Guiado por Teléfono',
|
||||
shortTitle: 'DESA Teléfono',
|
||||
category: 'desa',
|
||||
ageGroup: 'todos',
|
||||
description: 'Guía paso a paso para que un testigo use un DESA mientras se realiza RCP.',
|
||||
initialAssessment: [
|
||||
'¿Hay un DESA disponible cerca?',
|
||||
'¿Se está realizando RCP en este momento?',
|
||||
'¿La persona está en una superficie seca y segura?',
|
||||
],
|
||||
steps: [
|
||||
{
|
||||
id: 'continuar-rcp',
|
||||
order: 1,
|
||||
instruction: 'Mientras alguien trae el DESA, continúe con las compresiones torácicas sin parar.',
|
||||
verification: '¿Se están haciendo compresiones continuamente?',
|
||||
critical: true,
|
||||
notes: 'La RCP no debe parar mientras se prepara el DESA',
|
||||
},
|
||||
{
|
||||
id: 'encender-desa',
|
||||
order: 2,
|
||||
instruction: 'Cuando tenga el DESA, ábralo o pulse el botón de encendido. El dispositivo le dará instrucciones verbales. Siga sus indicaciones.',
|
||||
verification: '¿Está encendido el DESA y puede oír las instrucciones?',
|
||||
critical: true,
|
||||
},
|
||||
{
|
||||
id: 'preparar-torax',
|
||||
order: 3,
|
||||
instruction: 'Exponga el pecho de la persona. Si está mojado, séquelo rápidamente con una toalla. Si hay mucho vello, rasure la zona rápidamente si es posible.',
|
||||
verification: '¿Está el pecho expuesto, seco y sin vello que interfiera?',
|
||||
notes: 'Solo pare RCP por menos de 10 segundos para esta preparación',
|
||||
},
|
||||
{
|
||||
id: 'colocar-parches',
|
||||
order: 4,
|
||||
instruction: 'Retire los parches del DESA. Coloque un parche en el lado derecho del pecho, debajo de la clavícula, junto al esternón. Coloque el otro parche en el lado izquierdo, debajo y a la izquierda del pezón. Presione firmemente cada parche durante 5 segundos.',
|
||||
verification: '¿Están los parches colocados y bien pegados?',
|
||||
critical: true,
|
||||
notes: 'Colocación estándar: esternal (derecho) y apical (izquierdo)',
|
||||
},
|
||||
{
|
||||
id: 'conectar-cable',
|
||||
order: 5,
|
||||
instruction: 'Conecte el cable al DESA si no está pre-conectado. Debería encajarse con un clic.',
|
||||
verification: '¿Está el cable conectado correctamente?',
|
||||
},
|
||||
{
|
||||
id: 'analizar-ritmo',
|
||||
order: 6,
|
||||
instruction: 'El DESA le dirá "Alto, analizando ritmo" o similar. En ese momento, pare las compresiones y asegúrese de que NADIE toca a la persona. Grite "¡Todos fuera!" para asegurarse.',
|
||||
verification: '¿Está todo el mundo alejado de la persona?',
|
||||
critical: true,
|
||||
notes: 'Nadie debe tocar durante el análisis',
|
||||
},
|
||||
{
|
||||
id: 'seguir-ordenes',
|
||||
order: 7,
|
||||
instruction: 'El DESA analizará el ritmo. Siga exactamente las instrucciones que le dé.',
|
||||
verification: '¿Está escuchando las instrucciones del DESA?',
|
||||
critical: true,
|
||||
},
|
||||
{
|
||||
id: 'descarga-si-indicada',
|
||||
order: 8,
|
||||
instruction: 'Si el DESA dice "Se recomienda descarga" o similar, confirme en voz alta "DESCARGA RECOMENDADA". Asegúrese de que NADIE toca a la persona. Pulse el botón de descarga (parpadeante) y manténgalo pulsado hasta que se administre.',
|
||||
verification: '¿Está todo el mundo alejado antes de pulsar el botón de descarga?',
|
||||
critical: true,
|
||||
notes: 'Verificación final de seguridad antes de descargar',
|
||||
},
|
||||
{
|
||||
id: 'reanudar-rcp',
|
||||
order: 9,
|
||||
instruction: 'Inmediatamente después de la descarga (o si el DESA dice "No se recomienda descarga"), reanude las compresiones torácicas sin demora. Continúe con ciclos de 30 compresiones y 2 respiraciones.',
|
||||
verification: '¿Ha reanudado las compresiones inmediatamente?',
|
||||
critical: true,
|
||||
},
|
||||
{
|
||||
id: 'continuar-ciclos-desa',
|
||||
order: 10,
|
||||
instruction: 'El DESA le pedirá que pare cada 2 minutos para analizar de nuevo. Siga sus instrucciones. Continúe hasta que llegue la ambulancia o la persona empiece a respirar normalmente.',
|
||||
verification: '¿Está siguiendo las instrucciones del DESA?',
|
||||
critical: true,
|
||||
},
|
||||
],
|
||||
importantNotes: [
|
||||
'NUNCA toque a la persona durante el análisis o la descarga',
|
||||
'La RCP solo se para para analizar y descargar, no para colocar parches',
|
||||
'Si el DESA no recomienda descarga, reanude RCP inmediatamente',
|
||||
'El DESA guiará los ciclos completos de 2 minutos',
|
||||
],
|
||||
source: 'BLOQUE_04_4_USO_DESA.md',
|
||||
};
|
||||
|
||||
/**
|
||||
* Protocolo: OVACE Transtelefónica - Adultos
|
||||
* Fuente: BLOQUE_04_6_OVACE_ADULTOS.md (adaptado para guía telefónica)
|
||||
*/
|
||||
export const ovaceTelephoneAdult: TelephoneProtocol = {
|
||||
id: 'ovace-telephone-adult',
|
||||
title: 'OVACE Transtelefónica - Adultos',
|
||||
shortTitle: 'OVACE Adulto',
|
||||
category: 'ovace',
|
||||
ageGroup: 'adulto',
|
||||
description: 'Guía paso a paso para que un testigo resuelva una obstrucción de vía aérea en un adulto.',
|
||||
initialAssessment: [
|
||||
'¿La persona puede hablar o toser?',
|
||||
'¿Se está llevando las manos al cuello? (signo universal de atragantamiento)',
|
||||
'¿Está consciente o inconsciente?',
|
||||
'¿Es una persona obesa o embarazada?',
|
||||
],
|
||||
steps: [
|
||||
{
|
||||
id: 'evaluar-gravedad',
|
||||
order: 1,
|
||||
instruction: 'Primero, evalúe la gravedad. ¿Puede la persona hablar, toser o respirar?',
|
||||
verification: '¿Puede hablar/toser efectivamente o no puede?',
|
||||
critical: true,
|
||||
notes: 'Si puede toser efectivamente: animar a toser, no intervenir',
|
||||
},
|
||||
{
|
||||
id: 'ovace-leve',
|
||||
order: 2,
|
||||
instruction: 'Si puede toser con fuerza, anímela a seguir tosiendo. No haga nada más. La tos es el mejor mecanismo para desobstruir.',
|
||||
verification: '¿Está tosiendo con fuerza?',
|
||||
notes: 'Solo si la tos es efectiva',
|
||||
},
|
||||
{
|
||||
id: 'ovace-grave-consciente',
|
||||
order: 3,
|
||||
instruction: 'Si NO puede hablar, toser o respirar, y está consciente, colóquese de pie detrás de la persona. Rodee su cintura con sus brazos.',
|
||||
verification: '¿Está detrás de la persona con los brazos alrededor de su cintura?',
|
||||
critical: true,
|
||||
notes: 'Solo si está consciente y la obstrucción es grave',
|
||||
},
|
||||
{
|
||||
id: 'heimlich',
|
||||
order: 4,
|
||||
instruction: 'Coloque el puño (con el pulgar hacia dentro) en la línea media del abdomen, entre el ombligo y el final del esternón. Agarre su puño con la otra mano.',
|
||||
verification: '¿Tiene el puño en la posición correcta?',
|
||||
critical: true,
|
||||
},
|
||||
{
|
||||
id: 'compresiones-abdominales',
|
||||
order: 5,
|
||||
instruction: 'Aplique compresiones rápidas hacia dentro y hacia arriba, como si intentara levantar a la persona. Haga 5 compresiones separadas y distintas.',
|
||||
verification: '¿Está haciendo compresiones hacia dentro y hacia arriba?',
|
||||
critical: true,
|
||||
},
|
||||
{
|
||||
id: 'reevaluar',
|
||||
order: 6,
|
||||
instruction: 'Después de 5 compresiones, pregunte: "¿Ha salido? ¿Puede respirar?" Si puede hablar/toser efectivamente, pare. Si no, repita las 5 compresiones.',
|
||||
verification: '¿Puede ahora hablar o toser efectivamente?',
|
||||
critical: true,
|
||||
},
|
||||
{
|
||||
id: 'si-inconsciente',
|
||||
order: 7,
|
||||
instruction: 'Si la persona pierde el conocimiento, guíela al suelo con control. Active el 112 si no lo ha hecho. Inicie RCP inmediatamente (ver protocolo RCP).',
|
||||
verification: '¿Está inconsciente? ¿Ha activado el 112?',
|
||||
critical: true,
|
||||
notes: 'Si pierde consciencia, tratar como PCR y seguir protocolo RCP',
|
||||
},
|
||||
{
|
||||
id: 'embarazada-obeso',
|
||||
order: 8,
|
||||
instruction: 'Si la persona está embarazada o es obesa, haga compresiones TORÁCICAS (sobre el esternón) en lugar de abdominales. Use la misma técnica pero sobre el pecho.',
|
||||
verification: '¿Está haciendo compresiones sobre el pecho en lugar del abdomen?',
|
||||
notes: 'Solo si está embarazada o es obesa',
|
||||
},
|
||||
],
|
||||
importantNotes: [
|
||||
'Si puede toser efectivamente: NO intervenir, animar a toser',
|
||||
'Si está inconsciente: iniciar RCP inmediatamente',
|
||||
'En embarazadas/obesos: usar compresiones torácicas, no abdominales',
|
||||
'Después de desobstruir, siempre evaluar y considerar traslado',
|
||||
],
|
||||
source: 'BLOQUE_04_6_OVACE_ADULTOS.md',
|
||||
};
|
||||
|
||||
/**
|
||||
* Protocolo: SCA Transtelefónico
|
||||
* Fuente: Manual TES Digital (Bloque 5.6)
|
||||
*/
|
||||
export const scaTelephone: TelephoneProtocol = {
|
||||
id: 'sca-telephone',
|
||||
title: 'Sospecha de Síndrome Coronario Agudo (SCA)',
|
||||
shortTitle: 'SCA',
|
||||
category: 'sca',
|
||||
ageGroup: 'adulto',
|
||||
description: 'Reconocimiento de síntomas de SCA y primeros auxilios mientras llega la ambulancia.',
|
||||
initialAssessment: [
|
||||
'¿Tiene dolor en el pecho? ¿Cómo lo describe? (presión, opresión, ardor)',
|
||||
'¿El dolor se extiende a brazos, cuello, mandíbula o espalda?',
|
||||
'¿Tiene dificultad para respirar?',
|
||||
'¿Tiene náuseas, sudoración o mareo?',
|
||||
'¿Tiene antecedentes de problemas cardíacos?',
|
||||
],
|
||||
steps: [
|
||||
{
|
||||
id: 'activar-112-sca',
|
||||
order: 1,
|
||||
instruction: 'Active el servicio de emergencias inmediatamente. Llame al 112 y explique que sospecha un problema cardíaco.',
|
||||
verification: '¿Ha llamado al 112?',
|
||||
critical: true,
|
||||
},
|
||||
{
|
||||
id: 'posicion-comoda',
|
||||
order: 2,
|
||||
instruction: 'Coloque a la persona en una posición cómoda, preferiblemente semisentada o recostada con la cabeza elevada. Evite que se acueste completamente plano.',
|
||||
verification: '¿Está en una posición cómoda?',
|
||||
},
|
||||
{
|
||||
id: 'aflojar-ropa',
|
||||
order: 3,
|
||||
instruction: 'Afloje cualquier ropa ajustada, especialmente alrededor del cuello y el pecho.',
|
||||
verification: '¿Está la ropa aflojada?',
|
||||
},
|
||||
{
|
||||
id: 'aspirina-si-protocolo',
|
||||
order: 4,
|
||||
instruction: 'Si la persona está consciente, no es alérgica a la aspirina, y su protocolo local lo contempla, puede darle una aspirina (300 mg) para masticar. NO la trague entera.',
|
||||
verification: '¿Puede tomar aspirina? (Consulte si es alérgica)',
|
||||
notes: 'Solo si protocolo local contempla y no es alérgica',
|
||||
},
|
||||
{
|
||||
id: 'monitorizar',
|
||||
order: 5,
|
||||
instruction: 'Mantenga a la persona tranquila y monitorice su estado. Si pierde el conocimiento o deja de respirar, inicie RCP (ver protocolo RCP).',
|
||||
verification: '¿Está consciente y respirando?',
|
||||
critical: true,
|
||||
},
|
||||
{
|
||||
id: 'preparar-para-traslado',
|
||||
order: 6,
|
||||
instruction: 'Prepare a la persona para el traslado. No le dé de comer ni beber. Mantenga la calma y espere a la ambulancia.',
|
||||
verification: '¿Está preparado para cuando llegue la ambulancia?',
|
||||
},
|
||||
],
|
||||
importantNotes: [
|
||||
'El tiempo es crítico: activar 112 inmediatamente',
|
||||
'No dar aspirina si es alérgica o si protocolo local no lo contempla',
|
||||
'Si pierde consciencia o deja de respirar, iniciar RCP',
|
||||
'Mantener a la persona tranquila y en posición cómoda',
|
||||
],
|
||||
source: 'BLOQUE_05_6_SCA_TRANSTELEFONICO.md',
|
||||
};
|
||||
|
||||
/**
|
||||
* Protocolo: Ictus Transtelefónico - COMPLETO
|
||||
* Fuente: Capítulo 5.4 - Protocolo de Actuación Transtelefónica para Sospecha de Ictus Agudo
|
||||
* Versión: Expandida y detallada
|
||||
*/
|
||||
export const ictusTelephone: TelephoneProtocol = {
|
||||
id: 'ictus-telephone',
|
||||
title: 'Protocolo de Actuación Transtelefónica para Sospecha de Ictus Agudo',
|
||||
shortTitle: 'Ictus Transtelefónico',
|
||||
category: 'ictus',
|
||||
ageGroup: 'adulto',
|
||||
description: 'Protocolo completo para evaluación telefónica de sospecha de ictus agudo usando FAST/BE-FAST, determinación de urgencia, medidas de seguridad y preparación para traslado.',
|
||||
initialAssessment: [
|
||||
'¿Cuándo empezaron los síntomas? ¿A qué hora fue la ÚLTIMA VEZ que lo vio completamente normal?',
|
||||
'¿Puede sonreír normalmente? ¿Tiene la cara caída de un lado? (F - Face)',
|
||||
'¿Puede levantar ambos brazos? ¿Tiene un brazo débil o caído? (A - Arms)',
|
||||
'¿Puede hablar con claridad? ¿Tiene dificultad para hablar o habla arrastrado? (S - Speech)',
|
||||
'¿Tiene antecedentes: hipertensión, diabetes, fibrilación auricular o ictus previo?',
|
||||
'¿Es diabético? ¿Tiene glucómetro disponible? (para descartar hipoglucemia)',
|
||||
],
|
||||
steps: [
|
||||
{
|
||||
id: 'paso-0-control-inicial',
|
||||
order: 0,
|
||||
instruction: 'PASO 0: CONTROL INICIAL Y ACTIVACIÓN (primeros 30 segundos). Pregunte la dirección COMPLETA: calle, número, piso, puerta y localidad. Confirme puntos de referencia. Pregunte edad aproximada y antecedentes (HTA, diabetes, FA, ictus previo). Mientras evalúa, active el Código Ictus o equivalente. NO cuelgue, manténgase en línea.',
|
||||
verification: '¿Tiene la ubicación exacta? ¿Ha activado el Código Ictus?',
|
||||
critical: true,
|
||||
notes: 'Activación inmediata mientras se evalúa. Mantener línea abierta.',
|
||||
},
|
||||
{
|
||||
id: 'paso-1-fast-face',
|
||||
order: 1,
|
||||
instruction: 'PASO 1: EVALUACIÓN FAST - F (FACE/CARA). Instruya: "Pídale que SONRÍA o que enseñe los dientes." Pregunte: "¿Se le cae un lado de la cara? ¿La sonrisa es asimétrica o torcida?"',
|
||||
verification: '¿Hay asimetría facial clara? (F+)',
|
||||
critical: true,
|
||||
notes: 'Positivo si: un lado de la cara no se mueve igual que el otro',
|
||||
},
|
||||
{
|
||||
id: 'paso-1-fast-arms',
|
||||
order: 2,
|
||||
instruction: 'PASO 1: EVALUACIÓN FAST - A (ARMS/BRAZOS). Instruya: "Que levante los DOS brazos al frente, con las palmas hacia arriba y los ojos cerrados." Pregunte: "¿Se le cae uno de los brazos? ¿O no puede levantarlo?"',
|
||||
verification: '¿Un brazo cae, se desvía hacia abajo, o no puede levantarse? (A+)',
|
||||
critical: true,
|
||||
notes: 'Positivo si: un brazo cae, se desvía, o no puede levantarse',
|
||||
},
|
||||
{
|
||||
id: 'paso-1-fast-speech',
|
||||
order: 3,
|
||||
instruction: 'PASO 1: EVALUACIÓN FAST - S (SPEECH/HABLA). Instruya: "Que repita una frase simple: \'Hoy hace buen día\'." Pregunte: "¿Habla raro, arrastra las palabras, dice palabras incorrectas o no puede hablar?"',
|
||||
verification: '¿Hay disartria, afasia o mutismo? (S+)',
|
||||
critical: true,
|
||||
notes: 'Positivo si: disartria (arrastra palabras), afasia (palabras incorrectas), o mutismo',
|
||||
},
|
||||
{
|
||||
id: 'paso-1-fast-time',
|
||||
order: 4,
|
||||
instruction: 'PASO 1: EVALUACIÓN FAST - T (TIME/TIEMPO) - EL DATO MÁS CRÍTICO. Pregunta OBLIGATORIA: "¿A qué hora fue la ÚLTIMA VEZ QUE LO VIO COMPLETAMENTE NORMAL, sin ningún síntoma?" Si no hay testigo: "¿Cuándo comenzaron exactamente los síntomas?" Si se despertó con síntomas: "¿A qué hora se acostó estando normal?"',
|
||||
verification: '¿Sabe la hora exacta de última vez visto normal?',
|
||||
critical: true,
|
||||
notes: 'INFORMACIÓN MÁS CRÍTICA. Determina ventana terapéutica. Si no lo preguntas, puedes cerrar la puerta al tratamiento.',
|
||||
},
|
||||
{
|
||||
id: 'paso-2-be-fast-balance',
|
||||
order: 5,
|
||||
instruction: 'PASO 2: EVALUACIÓN BE-FAST (si FAST es negativo pero persiste sospecha). B (BALANCE/EQUILIBRIO): "¿Está muy mareado o tiene dificultad para mantenerse en pie o caminar?" Clave: Mareo AGUDO + otros signos neurológicos. No mareo aislado.',
|
||||
verification: '¿Mareo agudo con otros signos neurológicos?',
|
||||
notes: 'Solo si FAST negativo pero persiste sospecha clínica',
|
||||
},
|
||||
{
|
||||
id: 'paso-2-be-fast-eyes',
|
||||
order: 6,
|
||||
instruction: 'PASO 2: EVALUACIÓN BE-FAST - E (EYES/OJOS). "¿Ha perdido visión de repente en un ojo o en parte del campo visual? ¿Ve doble?" Clave: Pérdida visual SÚBITA, no progresiva.',
|
||||
verification: '¿Pérdida visual súbita?',
|
||||
notes: 'Solo si FAST negativo pero persiste sospecha clínica',
|
||||
},
|
||||
{
|
||||
id: 'paso-3-determinar-urgencia',
|
||||
order: 7,
|
||||
instruction: 'PASO 3: DETERMINACIÓN DE URGENCIA. Si FAST positivo O BE-FAST positivo con inicio <6h: ACTIVAR CÓDIGO ICTUS (Prioridad 1). Objetivos: Puerta-TC <25 min, Puerta-aguja <60 min. Transporte directo a Hospital con Unidad de Ictus.',
|
||||
verification: '¿Se ha activado Código Ictus? ¿Hospital con Unidad de Ictus notificado?',
|
||||
critical: true,
|
||||
notes: 'Criterios: FAST positivo, inicio <6h, déficit neurológico grave, cefalea explosiva + déficit, crisis convulsiva al inicio, anticoagulante con síntomas agudos',
|
||||
},
|
||||
{
|
||||
id: 'paso-4-posicionamiento',
|
||||
order: 8,
|
||||
instruction: 'PASO 4: POSICIONAMIENTO SEGURO. Si consciente, estable y sin vómitos: "Siéntelo o reclínelo semisentado, con la cabeza elevada a unos 30°." Si somnoliento o riesgo aspiración: "Póngalo de LADO (posición lateral de seguridad). Si hay un lado afectado, que quede ARRIBA." Si vómitos activos: "Siempre de lado, con la cabeza ligeramente baja." PROHIBICIÓN: NO sentar si somnoliento. NO acostar boca arriba si riesgo aspiración.',
|
||||
verification: '¿Está en posición segura según su estado?',
|
||||
critical: true,
|
||||
notes: 'La posición boca arriba mata por aspiración. La posición lateral salva vías aéreas.',
|
||||
},
|
||||
{
|
||||
id: 'paso-4-no-administracion',
|
||||
order: 9,
|
||||
instruction: 'PASO 4: PROHIBICIÓN ABSOLUTA. "NO coma ni beba NADA." (Alto riesgo disfagia/aspiración). "NO tome ninguna medicación por boca, incluida la aspirina." (Hasta determinar si es isquémico o hemorrágico). "NO administre insulina aunque sea diabético." (Solo si glucemia <70 mg/dL confirmada). "NO intente bajarle la tensión arterial." (Salvo TA >220/120 según protocolo).',
|
||||
verification: '¿Ha indicado claramente NADA por boca?',
|
||||
critical: true,
|
||||
notes: 'Nada por boca es absoluto. No aspirina hasta determinar tipo de ictus.',
|
||||
},
|
||||
{
|
||||
id: 'verificar-hipoglucemia',
|
||||
order: 10,
|
||||
instruction: 'DIFERENCIAL: HIPOGLUCEMIA (puede simular ictus). Preguntas obligatorias: "¿Es diabético? ¿Tiene un glucómetro disponible? ¿Tiene sudoración fría, temblor, sensación de hambre?" Si hay glucómetro: "Mida la glucemia ahora mismo." Si glucemia <70 mg/dL: Si consciente y traga seguro: "3 terrones de azúcar o zumo pequeño." Si no traga seguro o inconsciente: "NO le dé nada por la boca." NOTA: Corregir hipoglucemia no excluye ictus concurrente. Mantener sospecha.',
|
||||
verification: '¿Se ha descartado hipoglucemia?',
|
||||
notes: 'Hipoglucemia puede simular ictus. Verificar pero mantener sospecha si persiste',
|
||||
},
|
||||
{
|
||||
id: 'preparar-entorno',
|
||||
order: 11,
|
||||
instruction: 'PREPARACIÓN DEL ENTORNO. "Abra la puerta o deje el acceso libre para los equipos." "Prepare DNI, tarjeta sanitaria y lista de medicación HABITUAL." Especial énfasis: Anticoagulantes (Sintrom, Xarelto, Eliquis, Pradaxa). Antiagregantes (Adiro, Aspirina, Clopidogrel). "Si tiene informes médicos recientes, téngalos a mano." "Un acompañante debe PREPARARSE para ir al hospital."',
|
||||
verification: '¿Está preparado el entorno y la documentación?',
|
||||
},
|
||||
{
|
||||
id: 'monitorizacion-continua',
|
||||
order: 12,
|
||||
instruction: 'MONITORIZACIÓN CONTINUA (cada 2-3 minutos). Preguntas estandarizadas: "¿Sigue CONSCIENTE? ¿Responde a mi voz o a estímulos?" "¿RESPIRA con normalidad? ¿Escucha algún ruido raro?" "¿HA EMPEORADO la debilidad, el adormecimiento o el habla?" "¿TIENE VÓMITOS o el dolor de cabeza se ha hecho más fuerte?"',
|
||||
verification: '¿Está monitorizando cada 2-3 minutos?',
|
||||
critical: true,
|
||||
notes: 'Vigilancia activa hasta llegada de recursos. Detectar deterioro inmediatamente',
|
||||
},
|
||||
{
|
||||
id: 'criterios-deterioro-pcr',
|
||||
order: 13,
|
||||
instruction: 'CRITERIOS DE DETERIORO AGUDO (posible transición a PCR). Activar si: Pérdida brusca de consciencia (no responde). Respiración anormal (apnea, boqueos, estridor). Convulsiones activas prolongadas. Cianosis reportada ("se ha puesto morado"). CONDUCTA: "¿Responde si le toca o le habla fuerte? ¿Respira con normalidad?" Si NO responde o NO respira normal: "Póngalo en el suelo, boca arriba, sobre superficie dura. Empiece compresiones en el centro del pecho, fuerte y rápido." Transicionar inmediatamente al Protocolo RCP.',
|
||||
verification: '¿Está vigilando signos de deterioro?',
|
||||
critical: true,
|
||||
notes: 'Si deteriora a PCR, transicionar inmediatamente a protocolo RCP',
|
||||
},
|
||||
{
|
||||
id: 'traspaso-informacion',
|
||||
order: 14,
|
||||
instruction: 'INFORMACIÓN CRÍTICA PARA TRASPASO. Estructurar mensaje: HORA ÚLTIMA VEZ NORMAL: "[HH:MM]" o "desconocida". FAST/BE-FAST: Componentes positivos (Ej: "F y S positivos, A negativo"). GRAVEDAD: Breve descripción (Ej: "Paciente consciente pero afásico y con hemiparesia derecha"). ANTECEDENTES: "HTA, FA anticoagulada con Sintrom, diabetes". SÍNTOMAS ASOCIADOS: "Cefalea explosiva, vómitos x2". GLUCEMIA: "120 mg/dL" o "no medida". MEDIDAS TOMADAS: "Posicionado en lateral izquierda, nada por boca, documentación preparada".',
|
||||
verification: '¿Tiene toda la información crítica para el traspaso?',
|
||||
critical: true,
|
||||
notes: 'Mensaje estándar: "Código Ictus activado. Paciente [Edad/Sexo]: Última vez normal: [HH:MM] (hace X horas/minutos). FAST: [F+/A+/S+]. Déficit principal: [descripción]. Antecedentes clave: [lista]. TA actual: [valor] si se conoce. Llegada estimada: [HH:MM]."',
|
||||
},
|
||||
],
|
||||
importantNotes: [
|
||||
'🔴 TIEMPO ES CEREBRO: Cada minuto sin tratamiento destruye ~1,9 millones de neuronas. La evaluación telefónica debe ser más rápida que la progresión del infarto.',
|
||||
'🔴 NUNCA olvidar preguntar "¿Última vez visto normal?" - Es la información más crítica.',
|
||||
'🔴 NUNCA permitir que coma o beba - Riesgo de aspiración muy alto.',
|
||||
'🔴 NUNCA dejar somnoliento en decúbito supino - Siempre posición lateral.',
|
||||
'🔴 NUNCA minimizar síntomas atípicos (mareo, visión) especialmente en mayores, diabéticos o hipertensos.',
|
||||
'🔴 NUNCA retrasar activación del Código Ictus para "confirmar más" - TIEMPO = CEREBRO.',
|
||||
'🔴 NUNCA administrar o sugerir aspirina u otra medicación por teléfono - No se sabe si es isquémico o hemorrágico.',
|
||||
'🟢 SIEMPRE activar Código Ictus/prioridad máxima si FAST positivo y inicio <6h.',
|
||||
'🟢 SIEMPRE preguntar específicamente por medicación anticoagulante - Crítica para manejo hospitalario.',
|
||||
'🟢 SIEMPRE recomendar posición lateral de seguridad si hay cualquier disminución del nivel de conciencia.',
|
||||
'🟢 SIEMPRE indicar "Nada por boca" de forma absoluta y clara.',
|
||||
'🟢 SIEMPRE documentar con precisión: Hora última vez normal, componentes FAST, antecedentes clave.',
|
||||
'🟢 SIEMPRE preparar traspaso de información estructurado al equipo de ictus o al SVB.',
|
||||
'🟢 SIEMPRE vigilar continuamente nivel de consciencia y respiración hasta llegada de recursos.',
|
||||
'FAST negativo NO descarta ictus: Síntomas atípicos en pacientes de riesgo son ictus hasta demostrar lo contrario.',
|
||||
'El dato del tiempo es vital: La "última vez visto normal" determina la ventana terapéutica.',
|
||||
],
|
||||
source: 'Capítulo 5.4 - Protocolo de Actuación Transtelefónica para Sospecha de Ictus Agudo',
|
||||
};
|
||||
|
||||
/**
|
||||
* Lista de todos los protocolos transtelefónicos disponibles
|
||||
*/
|
||||
export const telephoneProtocols: TelephoneProtocol[] = [
|
||||
rcpTelephoneAdult,
|
||||
rcpTelephonePediatric,
|
||||
desaTelephone,
|
||||
ovaceTelephoneAdult,
|
||||
scaTelephone,
|
||||
ictusTelephone,
|
||||
];
|
||||
|
||||
/**
|
||||
* Obtener un protocolo por ID
|
||||
*/
|
||||
export function getProtocolById(id: string): TelephoneProtocol | undefined {
|
||||
return telephoneProtocols.find((protocol) => protocol.id === id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtener protocolos por categoría
|
||||
*/
|
||||
export function getProtocolsByCategory(category: ProtocolCategory): TelephoneProtocol[] {
|
||||
return telephoneProtocols.filter((protocol) => protocol.category === category);
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtener protocolos por grupo etario
|
||||
*/
|
||||
export function getProtocolsByAgeGroup(ageGroup: AgeGroup): TelephoneProtocol[] {
|
||||
return telephoneProtocols.filter((protocol) => protocol.ageGroup === ageGroup || protocol.ageGroup === 'todos');
|
||||
}
|
||||
17
backend/tsconfig.json
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2020",
|
||||
"module": "commonjs",
|
||||
"lib": ["ES2020"],
|
||||
"outDir": "./dist",
|
||||
"rootDir": "./src",
|
||||
"strict": true,
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"resolveJsonModule": true,
|
||||
"moduleResolution": "node"
|
||||
},
|
||||
"include": ["src/**/*"],
|
||||
"exclude": ["node_modules", "dist"]
|
||||
}
|
||||
50
docker-compose.yml
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
version: "3.8"
|
||||
|
||||
services:
|
||||
# Frontend - Puerto 9112
|
||||
codigo0-frontend:
|
||||
build:
|
||||
context: ./frontend
|
||||
dockerfile: Dockerfile
|
||||
container_name: codigo0-frontend
|
||||
restart: always
|
||||
ports:
|
||||
- "9112:9112"
|
||||
networks:
|
||||
- proxy
|
||||
environment:
|
||||
- VITE_API_URL=http://codigo0-backend:3000/api
|
||||
|
||||
# Backend - Puerto 3000
|
||||
codigo0-backend:
|
||||
build:
|
||||
context: ./backend
|
||||
dockerfile: Dockerfile
|
||||
container_name: codigo0-backend
|
||||
restart: always
|
||||
ports:
|
||||
- "3000:3000"
|
||||
networks:
|
||||
- proxy
|
||||
environment:
|
||||
- PORT=3000
|
||||
- NODE_ENV=production
|
||||
- MONGODB_URI=mongodb://mongodb:27017/codigo0
|
||||
- CORS_ORIGIN=*
|
||||
|
||||
# MongoDB
|
||||
mongodb:
|
||||
image: mongo:latest
|
||||
container_name: codigo0-mongodb
|
||||
restart: always
|
||||
volumes:
|
||||
- mongodb_data:/data/db
|
||||
networks:
|
||||
- proxy
|
||||
|
||||
networks:
|
||||
proxy:
|
||||
external: true
|
||||
|
||||
volumes:
|
||||
mongodb_data:
|
||||
1
frontend/.env.production
Normal file
|
|
@ -0,0 +1 @@
|
|||
VITE_API_URL=/api
|
||||
26
frontend/Dockerfile
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
# Etapa 1: Construcción del frontend
|
||||
FROM node:18-alpine AS build-stage
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Instalar dependencias
|
||||
COPY package*.json ./
|
||||
RUN npm install
|
||||
|
||||
# Copiar el resto de archivos y construir
|
||||
COPY . .
|
||||
RUN npm run build
|
||||
|
||||
# Etapa 2: Servidor Nginx para producción
|
||||
FROM nginx:alpine
|
||||
|
||||
# Copiar archivos construidos desde la etapa anterior
|
||||
COPY --from=build-stage /app/dist/ /usr/share/nginx/html/
|
||||
|
||||
# Copiar configuración personalizada de Nginx
|
||||
COPY nginx.conf /etc/nginx/conf.d/default.conf
|
||||
|
||||
# Exponer el puerto configurado en nginx.conf
|
||||
EXPOSE 9112
|
||||
|
||||
CMD ["nginx", "-g", "daemon off;"]
|
||||
15
frontend/docker-compose.yml
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
version: "3.8"
|
||||
|
||||
services:
|
||||
codigo0:
|
||||
build: .
|
||||
container_name: codigo0
|
||||
restart: always
|
||||
ports:
|
||||
- "9112:9112"
|
||||
networks:
|
||||
- proxy
|
||||
|
||||
networks:
|
||||
proxy:
|
||||
external: true
|
||||
23
frontend/index.html
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="es">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" href="/favicon.ico" type="image/x-icon" />
|
||||
<link rel="apple-touch-icon" sizes="192x192" href="/icon-192.png" />
|
||||
<link rel="apple-touch-icon" href="/icon-512.png" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
|
||||
<meta name="theme-color" content="#0f172a" />
|
||||
<meta name="description" content="Referencia rápida de protocolos de emergencias para Técnicos de Emergencias Sanitarias (TES). RCP, OVACE, Ictus, SCA, Shock y más." />
|
||||
<meta name="keywords" content="emergencias, TES, protocolos, RCP, OVACE, ambulancia, sanitario" />
|
||||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||
<meta name="apple-mobile-web-app-title" content="Código 0" />
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
|
||||
<meta name="mobile-web-app-capable" content="yes" />
|
||||
<link rel="manifest" href="/manifest.json" />
|
||||
<title>codigo0 — 0 Errores. 0 Dudas.</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/main.tsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
44
frontend/nginx.conf
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
server {
|
||||
listen 9112;
|
||||
server_name _;
|
||||
root /usr/share/nginx/html;
|
||||
index index.html;
|
||||
|
||||
# Gzip compression
|
||||
gzip on;
|
||||
gzip_vary on;
|
||||
gzip_min_length 1024;
|
||||
gzip_types text/plain text/css text/xml application/json application/javascript application/xml+rss application/atom+xml image/svg+xml;
|
||||
|
||||
# Cache estatic assets (JS/CSS/fonts)
|
||||
location ~* \.(js|css|woff2?|ttf|otf|eot|svg|ico|png|jpg|jpeg|webp)$ {
|
||||
expires 1y;
|
||||
add_header Cache-Control "public, immutable";
|
||||
try_files $uri =404;
|
||||
}
|
||||
|
||||
# Cache YAMLs de protocolos (corta duración para poder actualizarlos)
|
||||
location /protocols/ {
|
||||
expires 1d;
|
||||
add_header Cache-Control "public, must-revalidate";
|
||||
try_files $uri =404;
|
||||
}
|
||||
|
||||
# PWA: manifest y service worker sin caché
|
||||
location ~* (manifest\.json|sw\.js)$ {
|
||||
expires off;
|
||||
add_header Cache-Control "no-cache, no-store, must-revalidate";
|
||||
try_files $uri =404;
|
||||
}
|
||||
|
||||
# SPA fallback — todas las rutas sirven index.html
|
||||
location / {
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
|
||||
# Seguridad básica
|
||||
add_header X-Frame-Options "SAMEORIGIN";
|
||||
add_header X-Content-Type-Options "nosniff";
|
||||
add_header X-XSS-Protection "1; mode=block";
|
||||
add_header Referrer-Policy "strict-origin-when-cross-origin";
|
||||
}
|
||||
4959
frontend/package-lock.json
generated
Normal file
35
frontend/package.json
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
{
|
||||
"name": "codigo0-frontend",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"clsx": "^2.0.0",
|
||||
"hotkeys-js": "^3.13.4",
|
||||
"js-yaml": "^4.1.1",
|
||||
"lucide-react": "^0.263.1",
|
||||
"next-themes": "^0.2.1",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-markdown": "^10.1.0",
|
||||
"react-router-dom": "^6.15.0",
|
||||
"sonner": "^1.3.1",
|
||||
"tailwind-merge": "^1.14.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/js-yaml": "^4.0.9",
|
||||
"@types/react": "^18.2.21",
|
||||
"@types/react-dom": "^18.2.7",
|
||||
"@vitejs/plugin-react": "^4.0.4",
|
||||
"autoprefixer": "^10.4.15",
|
||||
"postcss": "^8.4.29",
|
||||
"tailwindcss": "^3.3.3",
|
||||
"typescript": "^5.2.2",
|
||||
"vite": "^4.4.9",
|
||||
"vite-plugin-svgr": "^3.2.0"
|
||||
}
|
||||
}
|
||||
6
frontend/postcss.config.js
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
export default {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
};
|
||||
2
frontend/public/assets/infografias/bloque-0-fundamentos/.svg
Executable file
|
After Width: | Height: | Size: 14 KiB |
|
|
@ -0,0 +1,20 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="800" height="600" viewBox="0 0 800 600">
|
||||
<rect width="800" height="600" fill="#f8f9fa"/>
|
||||
<rect x="20" y="20" width="760" height="560" fill="white" stroke="#dee2e6" stroke-width="2" rx="8"/>
|
||||
|
||||
<rect x="40" y="40" width="720" height="80" fill="#dc3545" rx="4"/>
|
||||
<text x="400" y="85" font-family="Arial, sans-serif" font-size="28" font-weight="bold" fill="white" text-anchor="middle">ERROR COMUN ABCDE #1</text>
|
||||
|
||||
<text x="400" y="160" font-family="Arial, sans-serif" font-size="32" font-weight="bold" fill="#212529" text-anchor="middle">SALTARSE LETRAS</text>
|
||||
<text x="400" y="220" font-family="Arial, sans-serif" font-size="18" fill="#495057" text-anchor="middle">Pasar de A directamente a C o D sin evaluar B</text>
|
||||
|
||||
<circle cx="400" cy="340" r="80" fill="#fee" stroke="#dc3545" stroke-width="3"/>
|
||||
<text x="400" y="360" font-family="Arial, sans-serif" font-size="60" fill="#dc3545" text-anchor="middle" font-weight="bold">A ’ D</text>
|
||||
<line x1="370" y1="320" x2="430" y2="380" stroke="#dc3545" stroke-width="4"/>
|
||||
<line x1="430" y1="320" x2="370" y2="380" stroke="#dc3545" stroke-width="4"/>
|
||||
|
||||
<rect x="100" y="450" width="600" height="100" fill="#fff3cd" stroke="#ffc107" stroke-width="2" rx="4"/>
|
||||
<text x="400" y="485" font-family="Arial, sans-serif" font-size="16" font-weight="bold" fill="#856404" text-anchor="middle">CONSECUENCIA</text>
|
||||
<text x="400" y="515" font-family="Arial, sans-serif" font-size="15" fill="#856404" text-anchor="middle">Puedes perder una hemorragia masiva (B)</text>
|
||||
<text x="400" y="540" font-family="Arial, sans-serif" font-size="15" fill="#856404" text-anchor="middle">o una obstruccion de via aerea (A)</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 292 KiB |
|
After Width: | Height: | Size: 142 KiB |
|
After Width: | Height: | Size: 211 KiB |
|
After Width: | Height: | Size: 152 KiB |
|
|
@ -0,0 +1,157 @@
|
|||
<svg width="1200" height="1600" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<linearGradient id="gradientA" x1="0%" y1="0%" x2="0%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#dc2626;stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:#ea580c;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
<linearGradient id="gradientB" x1="0%" y1="0%" x2="0%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#f59e0b;stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:#eab308;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
<linearGradient id="gradientC" x1="0%" y1="0%" x2="0%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#eab308;stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:#84cc16;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
<linearGradient id="gradientD" x1="0%" y1="0%" x2="0%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#22c55e;stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:#3b82f6;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
<linearGradient id="gradientE" x1="0%" y1="0%" x2="0%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#3b82f6;stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:#6366f1;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
|
||||
<!-- Fondo -->
|
||||
<rect width="1200" height="1600" fill="#1e293b"/>
|
||||
|
||||
<!-- Badge Modo Formación -->
|
||||
<rect x="50" y="30" width="300" height="50" rx="25" fill="#3b82f6" opacity="0.9"/>
|
||||
<text x="200" y="60" font-family="Arial, sans-serif" font-size="20" font-weight="bold" fill="white" text-anchor="middle">Modo Formación / Refuerzo</text>
|
||||
|
||||
<!-- Título Principal -->
|
||||
<text x="600" y="120" font-family="Arial, sans-serif" font-size="36" font-weight="bold" fill="#f1f5f9" text-anchor="middle">ABCDE: Estructura Mental de Priorización</text>
|
||||
<text x="600" y="160" font-family="Arial, sans-serif" font-size="24" fill="#94a3b8" text-anchor="middle">Lo que mata antes va primero</text>
|
||||
|
||||
<!-- Comparación: Lado Izquierdo (Desordenado) -->
|
||||
<g transform="translate(100, 220)">
|
||||
<rect x="0" y="0" width="450" height="600" rx="15" fill="#1e293b" stroke="#475569" stroke-width="2"/>
|
||||
<text x="225" y="40" font-family="Arial, sans-serif" font-size="22" font-weight="bold" fill="#ef4444" text-anchor="middle">Sin Estructura</text>
|
||||
<text x="225" y="70" font-family="Arial, sans-serif" font-size="18" fill="#f87171" text-anchor="middle">→ Errores de Prioridad</text>
|
||||
|
||||
<!-- Elementos caóticos -->
|
||||
<circle cx="100" cy="150" r="30" fill="#ef4444" opacity="0.6"/>
|
||||
<text x="100" y="160" font-family="Arial, sans-serif" font-size="16" font-weight="bold" fill="white" text-anchor="middle">?</text>
|
||||
|
||||
<circle cx="250" cy="120" r="25" fill="#f59e0b" opacity="0.6"/>
|
||||
<text x="250" y="130" font-family="Arial, sans-serif" font-size="14" font-weight="bold" fill="white" text-anchor="middle">!</text>
|
||||
|
||||
<circle cx="350" cy="180" r="28" fill="#eab308" opacity="0.6"/>
|
||||
<text x="350" y="190" font-family="Arial, sans-serif" font-size="15" font-weight="bold" fill="white" text-anchor="middle">?</text>
|
||||
|
||||
<circle cx="150" cy="250" r="22" fill="#ef4444" opacity="0.5"/>
|
||||
<text x="150" y="260" font-family="Arial, sans-serif" font-size="13" font-weight="bold" fill="white" text-anchor="middle">!</text>
|
||||
|
||||
<circle cx="300" cy="280" r="26" fill="#f59e0b" opacity="0.5"/>
|
||||
<text x="300" y="290" font-family="Arial, sans-serif" font-size="14" font-weight="bold" fill="white" text-anchor="middle">?</text>
|
||||
|
||||
<!-- Líneas caóticas -->
|
||||
<line x1="100" y1="150" x2="250" y2="120" stroke="#64748b" stroke-width="2" opacity="0.4"/>
|
||||
<line x1="250" y1="120" x2="350" y2="180" stroke="#64748b" stroke-width="2" opacity="0.4"/>
|
||||
<line x1="350" y1="180" x2="150" y2="250" stroke="#64748b" stroke-width="2" opacity="0.4"/>
|
||||
<line x1="150" y1="250" x2="300" y2="280" stroke="#64748b" stroke-width="2" opacity="0.4"/>
|
||||
|
||||
<!-- Icono de confusión -->
|
||||
<text x="225" y="450" font-family="Arial, sans-serif" font-size="80" fill="#64748b" opacity="0.5" text-anchor="middle">⚠</text>
|
||||
</g>
|
||||
|
||||
<!-- Comparación: Lado Derecho (Estructurado ABCDE) -->
|
||||
<g transform="translate(650, 220)">
|
||||
<rect x="0" y="0" width="450" height="600" rx="15" fill="#1e293b" stroke="#22c55e" stroke-width="3"/>
|
||||
<text x="225" y="40" font-family="Arial, sans-serif" font-size="22" font-weight="bold" fill="#22c55e" text-anchor="middle">Con Estructura ABCDE</text>
|
||||
<text x="225" y="70" font-family="Arial, sans-serif" font-size="18" fill="#4ade80" text-anchor="middle">→ Priorización Correcta</text>
|
||||
|
||||
<!-- A - Aire/Respiración (Más crítico) -->
|
||||
<rect x="125" y="100" width="200" height="80" rx="10" fill="url(#gradientA)"/>
|
||||
<text x="225" y="140" font-family="Arial, sans-serif" font-size="48" font-weight="bold" fill="white" text-anchor="middle">A</text>
|
||||
<text x="225" y="170" font-family="Arial, sans-serif" font-size="16" fill="white" text-anchor="middle">Aire / Respiración</text>
|
||||
<!-- Icono pulmones -->
|
||||
<circle cx="180" cy="130" r="15" fill="white" opacity="0.3"/>
|
||||
<circle cx="270" cy="130" r="15" fill="white" opacity="0.3"/>
|
||||
|
||||
<!-- Flecha descendente -->
|
||||
<polygon points="225,190 235,210 215,210" fill="#f1f5f9"/>
|
||||
|
||||
<!-- B - Circulación -->
|
||||
<rect x="135" y="220" width="180" height="70" rx="10" fill="url(#gradientB)"/>
|
||||
<text x="225" y="255" font-family="Arial, sans-serif" font-size="40" font-weight="bold" fill="white" text-anchor="middle">B</text>
|
||||
<text x="225" y="280" font-family="Arial, sans-serif" font-size="14" fill="white" text-anchor="middle">Circulación</text>
|
||||
<!-- Icono corazón -->
|
||||
<path d="M 200 240 Q 200 230, 210 240 Q 220 230, 240 240 Q 240 250, 225 260 Q 210 250, 200 240" fill="white" opacity="0.3"/>
|
||||
|
||||
<!-- Flecha descendente -->
|
||||
<polygon points="225,300 232,315 218,315" fill="#f1f5f9"/>
|
||||
|
||||
<!-- C - Cerebro/Consciencia -->
|
||||
<rect x="145" y="335" width="160" height="65" rx="10" fill="url(#gradientC)"/>
|
||||
<text x="225" y="370" font-family="Arial, sans-serif" font-size="36" font-weight="bold" fill="white" text-anchor="middle">C</text>
|
||||
<text x="225" y="390" font-family="Arial, sans-serif" font-size="13" fill="white" text-anchor="middle">Cerebro / Consciencia</text>
|
||||
<!-- Icono cerebro -->
|
||||
<ellipse cx="200" cy="360" rx="12" ry="8" fill="white" opacity="0.3"/>
|
||||
<ellipse cx="250" cy="360" rx="12" ry="8" fill="white" opacity="0.3"/>
|
||||
|
||||
<!-- Flecha descendente -->
|
||||
<polygon points="225,410 230,420 220,420" fill="#f1f5f9"/>
|
||||
|
||||
<!-- D - Exposición/Entorno -->
|
||||
<rect x="155" y="440" width="140" height="60" rx="10" fill="url(#gradientD)"/>
|
||||
<text x="225" y="470" font-family="Arial, sans-serif" font-size="32" font-weight="bold" fill="white" text-anchor="middle">D</text>
|
||||
<text x="225" y="490" font-family="Arial, sans-serif" font-size="12" fill="white" text-anchor="middle">Exposición / Entorno</text>
|
||||
<!-- Icono exposición -->
|
||||
<rect x="200" y="455" width="50" height="20" rx="3" fill="white" opacity="0.3"/>
|
||||
|
||||
<!-- Flecha descendente -->
|
||||
<polygon points="225,510 228,518 222,518" fill="#f1f5f9"/>
|
||||
|
||||
<!-- E - Evaluación Continua -->
|
||||
<rect x="165" y="535" width="120" height="55" rx="10" fill="url(#gradientE)"/>
|
||||
<text x="225" y="565" font-family="Arial, sans-serif" font-size="28" font-weight="bold" fill="white" text-anchor="middle">E</text>
|
||||
<text x="225" y="580" font-family="Arial, sans-serif" font-size="11" fill="white" text-anchor="middle">Evaluación Continua</text>
|
||||
<!-- Icono ciclo -->
|
||||
<circle cx="225" cy="560" r="8" fill="none" stroke="white" stroke-width="2" opacity="0.3"/>
|
||||
<path d="M 225 552 A 8 8 0 0 1 225 568" fill="none" stroke="white" stroke-width="2" opacity="0.3"/>
|
||||
</g>
|
||||
|
||||
<!-- Texto Explicativo Inferior -->
|
||||
<rect x="100" y="860" width="1000" height="200" rx="15" fill="#0f172a" stroke="#334155" stroke-width="2"/>
|
||||
<text x="600" y="900" font-family="Arial, sans-serif" font-size="20" font-weight="bold" fill="#f1f5f9" text-anchor="middle">Estructura Mental de Priorización</text>
|
||||
<text x="600" y="930" font-family="Arial, sans-serif" font-size="16" fill="#cbd5e1" text-anchor="middle" style="max-width: 900px;">
|
||||
El ABCDE no es un checklist, sino una estructura mental que organiza tu pensamiento
|
||||
</text>
|
||||
<text x="600" y="960" font-family="Arial, sans-serif" font-size="16" fill="#cbd5e1" text-anchor="middle" style="max-width: 900px;">
|
||||
bajo estrés. Cada nivel debe evaluarse antes de pasar al siguiente.
|
||||
</text>
|
||||
<text x="600" y="1000" font-family="Arial, sans-serif" font-size="18" font-weight="bold" fill="#22c55e" text-anchor="middle">Aplicable a TODAS las emergencias prehospitalarias</text>
|
||||
|
||||
<!-- Advertencia -->
|
||||
<rect x="100" y="1100" width="1000" height="120" rx="15" fill="#1e293b" stroke="#f59e0b" stroke-width="2"/>
|
||||
<text x="600" y="1135" font-family="Arial, sans-serif" font-size="18" font-weight="bold" fill="#fbbf24" text-anchor="middle">⚠ Esta guía es para formación y comprensión</text>
|
||||
<text x="600" y="1165" font-family="Arial, sans-serif" font-size="16" fill="#fcd34d" text-anchor="middle">Para acción inmediata, consulta el Protocolo Operativo ABCDE</text>
|
||||
|
||||
<!-- Badge Modo Operativo (inferior) -->
|
||||
<rect x="400" y="1250" width="400" height="50" rx="25" fill="#dc2626" opacity="0.9"/>
|
||||
<text x="600" y="1280" font-family="Arial, sans-serif" font-size="20" font-weight="bold" fill="white" text-anchor="middle">Modo Operativo</text>
|
||||
|
||||
<!-- Contexto visual: Escena de emergencia (sutil) -->
|
||||
<g opacity="0.2">
|
||||
<rect x="50" y="1350" width="1100" height="200" rx="10" fill="#334155"/>
|
||||
<circle cx="200" cy="1450" r="30" fill="#475569"/>
|
||||
<circle cx="1000" cy="1450" r="25" fill="#475569"/>
|
||||
<rect x="500" y="1420" width="200" height="60" rx="5" fill="#475569"/>
|
||||
</g>
|
||||
|
||||
<!-- Texto final -->
|
||||
<text x="600" y="1520" font-family="Arial, sans-serif" font-size="14" fill="#64748b" text-anchor="middle">Guía de Refuerzo - ABCDE Operativo</text>
|
||||
<text x="600" y="1550" font-family="Arial, sans-serif" font-size="12" fill="#475569" text-anchor="middle">Estructura mental de priorización para emergencias prehospitalarias</text>
|
||||
</svg>
|
||||
|
||||
|
After Width: | Height: | Size: 10 KiB |
|
|
@ -0,0 +1,218 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1000" height="1700" viewBox="0 0 1000 1700" role="img" aria-label="Diagrama de flujo START para triaje en emergencias (español)">
|
||||
<defs>
|
||||
<style>
|
||||
:root{
|
||||
--bg:#f7f9fb;
|
||||
--ink:#0f172a;
|
||||
--muted:#334155;
|
||||
--line:#94a3b8;
|
||||
--box:#ffffff;
|
||||
|
||||
--red:#dc2626;
|
||||
--yellow:#f59e0b;
|
||||
--green:#16a34a;
|
||||
--black:#111827;
|
||||
|
||||
--accent:#2563eb;
|
||||
}
|
||||
.title{font:800 34px/1.2 system-ui,-apple-system,Segoe UI,Roboto,Helvetica,Arial,sans-serif; fill:var(--ink);}
|
||||
.subtitle{font:500 16px/1.4 system-ui,-apple-system,Segoe UI,Roboto,Helvetica,Arial,sans-serif; fill:var(--muted);}
|
||||
.hdr{font:800 20px/1.2 system-ui,-apple-system,Segoe UI,Roboto,Helvetica,Arial,sans-serif; fill:var(--ink);}
|
||||
.txt{font:500 16px/1.5 system-ui,-apple-system,Segoe UI,Roboto,Helvetica,Arial,sans-serif; fill:var(--muted);}
|
||||
.small{font:500 13px/1.4 system-ui,-apple-system,Segoe UI,Roboto,Helvetica,Arial,sans-serif; fill:var(--muted);}
|
||||
|
||||
.box{fill:var(--box); stroke:var(--line); stroke-width:2;}
|
||||
.boxStrong{fill:var(--box); stroke:var(--accent); stroke-width:2.5;}
|
||||
.diamond{fill:var(--box); stroke:var(--line); stroke-width:2.5;}
|
||||
.diamondRed{fill:var(--box); stroke:var(--red); stroke-width:2.8;}
|
||||
.arrow{stroke:var(--line); stroke-width:3; fill:none; marker-end:url(#arrowHead);}
|
||||
.arrowAccent{stroke:var(--accent); stroke-width:3.5; fill:none; marker-end:url(#arrowHeadAccent);}
|
||||
|
||||
.pillText{font:800 15px/1 system-ui,-apple-system,Segoe UI,Roboto,Helvetica,Arial,sans-serif; fill:#fff;}
|
||||
.legendText{font:700 14px/1.2 system-ui,-apple-system,Segoe UI,Roboto,Helvetica,Arial,sans-serif; fill:var(--ink);}
|
||||
|
||||
.tagRed{fill:var(--red);}
|
||||
.tagYellow{fill:var(--yellow);}
|
||||
.tagGreen{fill:var(--green);}
|
||||
.tagBlack{fill:var(--black);}
|
||||
|
||||
.iconStroke{stroke:var(--muted); stroke-width:2.2; fill:none; stroke-linecap:round; stroke-linejoin:round;}
|
||||
</style>
|
||||
|
||||
<marker id="arrowHead" markerWidth="10" markerHeight="10" refX="9" refY="5" orient="auto">
|
||||
<path d="M0,0 L10,5 L0,10 Z" fill="#94a3b8"/>
|
||||
</marker>
|
||||
<marker id="arrowHeadAccent" markerWidth="10" markerHeight="10" refX="9" refY="5" orient="auto">
|
||||
<path d="M0,0 L10,5 L0,10 Z" fill="#2563eb"/>
|
||||
</marker>
|
||||
|
||||
<filter id="shadow" x="-20%" y="-20%" width="140%" height="140%">
|
||||
<feDropShadow dx="0" dy="2" stdDeviation="3" flood-color="#0f172a" flood-opacity="0.12"/>
|
||||
</filter>
|
||||
</defs>
|
||||
|
||||
<!-- Background -->
|
||||
<rect x="0" y="0" width="1000" height="1600" fill="var(--bg)"/>
|
||||
|
||||
<!-- Header -->
|
||||
<text x="500" y="70" text-anchor="middle" class="title">Triage START (Simple Triage and Rapid Treatment)</text>
|
||||
<text x="500" y="105" text-anchor="middle" class="subtitle">Algoritmo de clasificación rápida en incidentes con múltiples víctimas</text>
|
||||
|
||||
<!-- Legend -->
|
||||
<g filter="url(#shadow)">
|
||||
<rect x="140" y="130" rx="16" ry="16" width="720" height="70" class="box"/>
|
||||
</g>
|
||||
<g transform="translate(170,152)">
|
||||
<rect x="0" y="0" rx="12" ry="12" width="140" height="36" class="tagRed"/>
|
||||
<text x="70" y="24" text-anchor="middle" class="pillText">ROJO</text>
|
||||
<text x="160" y="24" class="legendText">Inmediato</text>
|
||||
|
||||
<rect x="290" y="0" rx="12" ry="12" width="140" height="36" class="tagYellow"/>
|
||||
<text x="360" y="24" text-anchor="middle" class="pillText">AMARILLO</text>
|
||||
<text x="450" y="24" class="legendText">Urgente</text>
|
||||
|
||||
<rect x="560" y="0" rx="12" ry="12" width="140" height="36" class="tagGreen"/>
|
||||
<text x="630" y="24" text-anchor="middle" class="pillText">VERDE</text>
|
||||
<text x="720" y="24" class="legendText">Leve</text>
|
||||
</g>
|
||||
<g transform="translate(170,188)">
|
||||
<rect x="0" y="0" rx="12" ry="12" width="140" height="30" class="tagBlack"/>
|
||||
<text x="70" y="20" text-anchor="middle" class="pillText" style="font-size:14px;">NEGRO</text>
|
||||
<text x="160" y="20" class="legendText">Fallecido / Expectante</text>
|
||||
</g>
|
||||
|
||||
<!-- Start box -->
|
||||
<g filter="url(#shadow)">
|
||||
<rect x="220" y="240" rx="18" ry="18" width="560" height="90" class="boxStrong"/>
|
||||
</g>
|
||||
<text x="500" y="275" text-anchor="middle" class="hdr">INICIO</text>
|
||||
<text x="500" y="305" text-anchor="middle" class="txt">Evaluación rápida por víctima (≈ 30–60 s). Aplicar medidas básicas si procede.</text>
|
||||
|
||||
<path class="arrowAccent" d="M500,330 L500,385"/>
|
||||
|
||||
<!-- Decision 1: ¿Camina? -->
|
||||
<g filter="url(#shadow)">
|
||||
<polygon class="diamond" points="500,390 645,505 500,620 355,505"/>
|
||||
</g>
|
||||
<text x="500" y="495" text-anchor="middle" class="hdr">¿Camina?</text>
|
||||
<text x="500" y="525" text-anchor="middle" class="small">(deambula por sí mismo)</text>
|
||||
|
||||
<!-- YES -> Verde -->
|
||||
<text x="700" y="500" class="txt" style="fill:var(--green); font-weight:700;">Sí</text>
|
||||
<path class="arrow" d="M645,505 C735,505 785,505 860,505"/>
|
||||
|
||||
<g filter="url(#shadow)">
|
||||
<rect x="865" y="460" rx="16" ry="16" width="120" height="90" class="box"/>
|
||||
</g>
|
||||
<rect x="875" y="472" rx="12" ry="12" width="100" height="30" class="tagGreen"/>
|
||||
<text x="925" y="493" text-anchor="middle" class="pillText">VERDE</text>
|
||||
<text x="925" y="525" text-anchor="middle" class="small">Leve</text>
|
||||
|
||||
<!-- NO -> next -->
|
||||
<text x="300" y="500" class="txt" style="font-weight:700;">No</text>
|
||||
<path class="arrowAccent" d="M500,620 L500,675"/>
|
||||
|
||||
<!-- Decision 2: Respiración presente -->
|
||||
<g filter="url(#shadow)">
|
||||
<polygon class="diamondRed" points="500,680 660,810 500,940 340,810"/>
|
||||
</g>
|
||||
<text x="500" y="800" text-anchor="middle" class="hdr">¿Respira?</text>
|
||||
<text x="500" y="830" text-anchor="middle" class="small">Si NO: abrir vía aérea</text>
|
||||
|
||||
<!-- No breathing -> Negro -->
|
||||
<text x="695" y="805" class="txt" style="font-weight:700;">No</text>
|
||||
<path class="arrow" d="M660,810 C745,810 805,810 860,810"/>
|
||||
<g filter="url(#shadow)">
|
||||
<rect x="865" y="765" rx="16" ry="16" width="120" height="105" class="box"/>
|
||||
</g>
|
||||
<rect x="875" y="777" rx="12" ry="12" width="100" height="30" class="tagBlack"/>
|
||||
<text x="925" y="798" text-anchor="middle" class="pillText">NEGRO</text>
|
||||
<text x="925" y="830" text-anchor="middle" class="small">Fallecido</text>
|
||||
<text x="925" y="852" text-anchor="middle" class="small">/ expectante</text>
|
||||
|
||||
<!-- If starts breathing after opening airway -> Rojo -->
|
||||
<text x="300" y="805" class="txt" style="font-weight:700;">Sí</text>
|
||||
<path class="arrowAccent" d="M500,940 L500,990"/>
|
||||
|
||||
<!-- Decision 3: FR > 30 -->
|
||||
<g filter="url(#shadow)">
|
||||
<polygon class="diamond" points="500,995 660,1125 500,1255 340,1125"/>
|
||||
</g>
|
||||
<text x="500" y="1115" text-anchor="middle" class="hdr">¿FR > 30 rpm?</text>
|
||||
<text x="500" y="1145" text-anchor="middle" class="small">(frecuencia respiratoria)</text>
|
||||
|
||||
<!-- Yes -> Rojo -->
|
||||
<text x="695" y="1120" class="txt" style="fill:var(--red); font-weight:700;">Sí</text>
|
||||
<path class="arrow" d="M660,1125 C745,1125 805,1125 860,1125"/>
|
||||
<g filter="url(#shadow)">
|
||||
<rect x="865" y="1080" rx="16" ry="16" width="120" height="90" class="box"/>
|
||||
</g>
|
||||
<rect x="875" y="1092" rx="12" ry="12" width="100" height="30" class="tagRed"/>
|
||||
<text x="925" y="1113" text-anchor="middle" class="pillText">ROJO</text>
|
||||
<text x="925" y="1145" text-anchor="middle" class="small">Inmediato</text>
|
||||
|
||||
<!-- No -> Perfusion -->
|
||||
<text x="300" y="1120" class="txt" style="font-weight:700;">No</text>
|
||||
<path class="arrowAccent" d="M500,1255 L500,1310"/>
|
||||
|
||||
<!-- Decision 4: Perfusión -->
|
||||
<g filter="url(#shadow)">
|
||||
<polygon class="diamond" points="500,1315 660,1445 500,1575 340,1445"/>
|
||||
</g>
|
||||
<text x="500" y="1420" text-anchor="middle" class="hdr">Perfusión adecuada?</text>
|
||||
<text x="500" y="1448" text-anchor="middle" class="small">Relleno capilar ≤ 2 s y pulso radial presente</text>
|
||||
|
||||
<!-- If inadequate -> Rojo -->
|
||||
<text x="695" y="1440" class="txt" style="fill:var(--red); font-weight:700;">No</text>
|
||||
<path class="arrow" d="M660,1445 C745,1445 805,1445 860,1445"/>
|
||||
<g filter="url(#shadow)">
|
||||
<rect x="865" y="1400" rx="16" ry="16" width="120" height="90" class="box"/>
|
||||
</g>
|
||||
<rect x="875" y="1412" rx="12" ry="12" width="100" height="30" class="tagRed"/>
|
||||
<text x="925" y="1433" text-anchor="middle" class="pillText">ROJO</text>
|
||||
<text x="925" y="1465" text-anchor="middle" class="small">Inmediato</text>
|
||||
|
||||
<!-- If adequate -> Mental -->
|
||||
<text x="300" y="1440" class="txt" style="fill:var(--green); font-weight:700;">Sí</text>
|
||||
|
||||
<!-- Mental status box (placed below with connector) -->
|
||||
<!-- Arrow down from perfusion diamond to mental box -->
|
||||
<path class="arrowAccent" d="M500,1575 L500,1605"/>
|
||||
<!-- expand viewBox height already fixed; so place mental box a bit higher within area by shifting diamond up? Not possible.
|
||||
We'll place mental status box above perfusion diamond and connect from its bottom-left? Instead: add side branch to left below diamond (fits).
|
||||
-->
|
||||
<!-- Replace: Side branch to left for mental status -->
|
||||
<path class="arrowAccent" d="M340,1445 C250,1445 215,1445 170,1445"/>
|
||||
<g filter="url(#shadow)">
|
||||
<rect x="20" y="1370" rx="18" ry="18" width="300" height="150" class="box"/>
|
||||
</g>
|
||||
<text x="170" y="1405" text-anchor="middle" class="hdr">Estado mental</text>
|
||||
<text x="170" y="1436" text-anchor="middle" class="small">¿Obedece órdenes?</text>
|
||||
<text x="170" y="1465" text-anchor="middle" class="small">• No obedece → ROJO</text>
|
||||
<text x="170" y="1492" text-anchor="middle" class="small">• Obedece → AMARILLO</text>
|
||||
|
||||
<!-- From mental box to outcomes -->
|
||||
<path class="arrow" d="M320,1415 C375,1415 400,1415 430,1415"/>
|
||||
<text x="345" y="1395" class="small" style="fill:var(--red); font-weight:700;">No</text>
|
||||
<g filter="url(#shadow)">
|
||||
<rect x="435" y="1368" rx="16" ry="16" width="120" height="85" class="box"/>
|
||||
</g>
|
||||
<rect x="445" y="1380" rx="12" ry="12" width="100" height="30" class="tagRed"/>
|
||||
<text x="495" y="1401" text-anchor="middle" class="pillText">ROJO</text>
|
||||
<text x="495" y="1432" text-anchor="middle" class="small">Inmediato</text>
|
||||
|
||||
<path class="arrow" d="M320,1478 C380,1478 420,1478 480,1478"/>
|
||||
<text x="350" y="1502" class="small" style="fill:var(--green); font-weight:700;">Sí</text>
|
||||
<g filter="url(#shadow)">
|
||||
<rect x="485" y="1435" rx="16" ry="16" width="140" height="90" class="box"/>
|
||||
</g>
|
||||
<rect x="495" y="1447" rx="12" ry="12" width="120" height="30" class="tagYellow"/>
|
||||
<text x="555" y="1468" text-anchor="middle" class="pillText">AMARILLO</text>
|
||||
<text x="555" y="1500" text-anchor="middle" class="small">Urgente</text>
|
||||
|
||||
<!-- Footer note -->
|
||||
<text x="500" y="1660" text-anchor="middle" class="small">
|
||||
Nota: En ausencia de respiración, abrir vía aérea. Si no respira tras apertura → NEGRO; si inicia respiración → ROJO.
|
||||
</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 118 KiB |
|
After Width: | Height: | Size: 2.9 MiB |
|
After Width: | Height: | Size: 123 KiB |
|
After Width: | Height: | Size: 174 KiB |
BIN
frontend/public/assets/infografias/bloque-0-fundamentos/fast_transtelefonico.png
Executable file
|
After Width: | Height: | Size: 153 KiB |
BIN
frontend/public/assets/infografias/bloque-0-fundamentos/flujo_desa_telefono.png
Executable file
|
After Width: | Height: | Size: 256 KiB |
|
After Width: | Height: | Size: 245 KiB |
|
After Width: | Height: | Size: 260 KiB |
BIN
frontend/public/assets/infografias/bloque-0-fundamentos/ovace_lactantes.png
Executable file
|
After Width: | Height: | Size: 206 KiB |
BIN
frontend/public/assets/infografias/bloque-0-fundamentos/ovace_pediatrica.png
Executable file
|
After Width: | Height: | Size: 196 KiB |
|
After Width: | Height: | Size: 2.9 MiB |
|
After Width: | Height: | Size: 157 KiB |
|
After Width: | Height: | Size: 156 KiB |
|
After Width: | Height: | Size: 2.6 MiB |
|
After Width: | Height: | Size: 74 KiB |
|
After Width: | Height: | Size: 143 KiB |
BIN
frontend/public/assets/infografias/bloque-0-fundamentos/tabla_escala_glasgow.png
Executable file
|
After Width: | Height: | Size: 112 KiB |
|
After Width: | Height: | Size: 175 KiB |
|
After Width: | Height: | Size: 118 KiB |
|
After Width: | Height: | Size: 96 KiB |
BIN
frontend/public/assets/infografias/bloque-0-fundamentos/vias_administracion.png
Executable file
|
After Width: | Height: | Size: 166 KiB |
|
After Width: | Height: | Size: 232 KiB |
|
After Width: | Height: | Size: 150 KiB |
BIN
frontend/public/assets/infografias/bloque-2-inmovilizacion/.png
Executable file
|
After Width: | Height: | Size: 129 KiB |
|
After Width: | Height: | Size: 239 KiB |
|
After Width: | Height: | Size: 208 KiB |
|
After Width: | Height: | Size: 197 KiB |
|
After Width: | Height: | Size: 207 KiB |
|
After Width: | Height: | Size: 112 KiB |
|
After Width: | Height: | Size: 122 KiB |
|
After Width: | Height: | Size: 108 KiB |
|
After Width: | Height: | Size: 177 KiB |
|
After Width: | Height: | Size: 173 KiB |
|
After Width: | Height: | Size: 229 KiB |
|
After Width: | Height: | Size: 184 KiB |
|
After Width: | Height: | Size: 153 KiB |
|
After Width: | Height: | Size: 120 KiB |
|
After Width: | Height: | Size: 118 KiB |
|
After Width: | Height: | Size: 160 KiB |
|
After Width: | Height: | Size: 220 KiB |
|
After Width: | Height: | Size: 195 KiB |
|
After Width: | Height: | Size: 165 KiB |
|
After Width: | Height: | Size: 152 KiB |
|
After Width: | Height: | Size: 174 KiB |
|
After Width: | Height: | Size: 178 KiB |
|
After Width: | Height: | Size: 114 KiB |