// Plan de cuidados — single-of-each model with strict selection limits. // • 1 diagnóstico (≤4 características, ≤1 factor) // • 1 objetivo (N indicadores, Diana basal→meta usando la escala del propio objetivo) // • 1 intervención (≤10 actividades) // Relations between dx → obj/int are content-based (built offline in data/relations.json). const { useState: pUS, useEffect: pUE, useMemo: pUM, useRef: pUR } = React; // Limits const MAX_CARS = 4; const MAX_FACTORS = 1; const MAX_INDICATORS = 5; const MAX_ACTIVITIES = 10; // ─── Diagnostic type detection ────────────────────────────── function detectDxType(etiqueta = '') { const e = etiqueta.toLowerCase(); if (/^riesgo\s+/.test(e)) return 'riesgo'; if (/^disposici[oó]n\s+para\s+mejorar/.test(e) || /^disposici[oó]n\s+para/.test(e)) return 'promocion'; if (/^s[ií]ndrome\s+/.test(e)) return 'sindrome'; return 'real'; } function dxTypeLabel(t) { return { real: 'Diagnóstico real (enfocado al problema)', riesgo: 'Diagnóstico de riesgo', promocion: 'Diagnóstico de promoción de la salud', sindrome: 'Diagnóstico de síndrome', }[t]; } function dxTypeStructure(t) { return { real: 'PES — Etiqueta + relacionado con + factor + manifestado por + características', riesgo: 'PE — Etiqueta + relacionado con + factor de riesgo', promocion: 'Etiqueta + manifestado por + signos de deseo de mejora', sindrome: 'Etiqueta del síndrome + relacionado con + factor relacionado', }[t]; } function buildDxSentence(dx) { if (!dx) return ''; const etiqueta = dx.etiqueta || ''; const factores = (dx.selectedFactores || []).filter(Boolean); const defin = (dx.selectedDefinitorias || []).filter(Boolean); const type = dx.type || detectDxType(etiqueta); const joinList = (arr) => { if (!arr.length) return '___'; if (arr.length === 1) return arr[0].toLowerCase(); if (arr.length === 2) return arr.join(' y ').toLowerCase(); return arr.slice(0, -1).join(', ').toLowerCase() + ' y ' + arr.slice(-1)[0].toLowerCase(); }; if (type === 'real') { return `${etiqueta} relacionado con ${joinList(factores)}, manifestado por ${joinList(defin)}.`; } if (type === 'riesgo') { return `${etiqueta} relacionado con ${joinList(factores)}.`; } if (type === 'promocion') { return `${etiqueta} manifestado por ${joinList(defin)}.`; } return `${etiqueta} relacionado con ${joinList(factores)}.`; } // ─── Shared store ─────────────────────────────────────────── const PlanStore = { state: { dx: null, obj: null, int: null }, listeners: new Set(), set(updater) { this.state = typeof updater === 'function' ? updater(this.state) : { ...this.state, ...updater }; this.listeners.forEach(fn => fn(this.state)); }, use() { const [, force] = pUS(0); pUE(() => { const fn = () => force(t => t + 1); this.listeners.add(fn); return () => this.listeners.delete(fn); }, []); return [this.state, (u) => this.set(u)]; }, reset() { this.set({ dx: null, obj: null, int: null }); }, }; // ─── Mutators ────────────────────────────────────────────── function setPlanDx(dx) { const type = detectDxType(dx.etiqueta); PlanStore.set({ dx: { codigo: dx.codigo, etiqueta: dx.etiqueta, definicion: dx.definicion, dominio: dx.dominio, dominio_num: dx.dominio_num, clase: dx.clase, clase_num: dx.clase_num, type, selectedDefinitorias: [], selectedFactores: [], manifestaciones: '', } }); } // Texto libre de manifestaciones clínicas (valoración del profesional) function updatePlanDxManifestaciones(text) { PlanStore.set(s => s.dx ? ({ ...s, dx: { ...s.dx, manifestaciones: text } }) : s); } function togglePlanDxItem(group, value) { const limit = group === 'selectedFactores' ? MAX_FACTORS : MAX_CARS; PlanStore.set(s => { if (!s.dx) return s; const list = s.dx[group] || []; let next; if (list.includes(value)) { next = list.filter(v => v !== value); } else { if (list.length >= limit) { // Auto-evict oldest when at limit. next = [...list.slice(1), value]; } else { next = [...list, value]; } } return { ...s, dx: { ...s.dx, [group]: next } }; }); } function setPlanObjective(obj) { PlanStore.set({ obj: { codigo: obj.codigo, titulo: obj.titulo, definicion: obj.definicion, dominio: obj.dominio, dominio_num: obj.dominio_num, clase: obj.clase, clase_codigo: obj.clase_codigo, escala: obj.escala || [], indicadores: obj.indicadores || [], selectedIndicadores: [], indScores: {}, // { [codigo]: { basal, meta } } — escala individual por indicador basal: 1, meta: 5, } }); } function clearPlanObjective() { PlanStore.set({ obj: null }); } // Diana por defecto según número de niveles de la escala (basal bajo, meta alta) function defaultScores(escala) { const n = (escala && escala.length) || 5; return { basal: Math.min(3, Math.max(1, Math.round(n / 2))), meta: n }; } function togglePlanObjectiveIndicator(codigo) { PlanStore.set(s => { if (!s.obj) return s; const sel = s.obj.selectedIndicadores || []; const scores = { ...(s.obj.indScores || {}) }; if (sel.includes(codigo)) { delete scores[codigo]; return { ...s, obj: { ...s.obj, selectedIndicadores: sel.filter(v => v !== codigo), indScores: scores } }; } if (sel.length >= MAX_INDICATORS) return s; // hard cap scores[codigo] = defaultScores(s.obj.escala); return { ...s, obj: { ...s.obj, selectedIndicadores: [...sel, codigo], indScores: scores } }; }); } // Actualiza la escala basal/meta de UN indicador concreto function updatePlanObjectiveIndicator(codigo, patch) { PlanStore.set(s => { if (!s.obj) return s; const scores = { ...(s.obj.indScores || {}) }; scores[codigo] = { ...(scores[codigo] || defaultScores(s.obj.escala)), ...patch }; return { ...s, obj: { ...s.obj, indScores: scores } }; }); } function updatePlanObjective(patch) { PlanStore.set(s => s.obj ? ({ ...s, obj: { ...s.obj, ...patch } }) : s); } function setPlanIntervention(int) { PlanStore.set({ int: { codigo: int.codigo, nombre: int.nombre, dominio: int.dominio, dominio_num: int.dominio_num, clase: int.clase, clase_codigo: int.clase_codigo, definicion: int.definicion, actividades: int.actividades || [], selectedActividades: [], } }); } function clearPlanIntervention() { PlanStore.set({ int: null }); } function togglePlanInterventionActivity(value) { PlanStore.set(s => { if (!s.int) return s; const sel = s.int.selectedActividades || []; if (sel.includes(value)) { return { ...s, int: { ...s.int, selectedActividades: sel.filter(v => v !== value) } }; } if (sel.length >= MAX_ACTIVITIES) return s; // hard cap return { ...s, int: { ...s.int, selectedActividades: [...sel, value] } }; }); } // ─── Plan readiness flags ─────────────────────────────────── function isDxReady(dx) { if (!dx) return false; const t = dx.type; if (t === 'riesgo' || t === 'sindrome') return dx.selectedFactores?.length > 0; if (t === 'promocion') return dx.selectedDefinitorias?.length > 0; return dx.selectedDefinitorias?.length > 0 && dx.selectedFactores?.length > 0; } function isObjReady(obj) { return !!obj && obj.selectedIndicadores?.length > 0; } function isIntReady(intv) { return !!intv && intv.selectedActividades?.length > 0; } // ─── Inline Plan Summary (right rail) ─────────────────────── function PlanSummary({ onOpenFinal, onGoToKind, currentKind }) { const [plan] = PlanStore.use(); const dxReady = isDxReady(plan.dx); const objReady = isObjReady(plan.obj); const intReady = isIntReady(plan.int); const ready = dxReady && objReady && intReady; const completed = [dxReady, objReady, intReady].filter(Boolean).length; return ( ); } // ─── Floating Plan Widget (legacy — kept for fallback at narrow widths) ── function PlanWidget({ onOpenFinal }) { const [plan] = PlanStore.use(); const [expanded, setExpanded] = pUS(false); const dxReady = isDxReady(plan.dx); const objReady = isObjReady(plan.obj); const intReady = isIntReady(plan.int); const ready = dxReady && objReady && intReady; if (!plan.dx && !plan.obj && !plan.int) return null; return (
|
DIAGNÓSTICO DE ENFERMERÍA:
{sentence}
{dx.manifestaciones && dx.manifestaciones.trim() && (
Manifestaciones: {dx.manifestaciones.trim()}
)}
|
Dominio: {dominioNum}{dominioName ? `, ${dominioName.toLowerCase()}` : ''}
|
Clase: {claseNum}{claseName ? `, ${claseName.toLowerCase()}` : ''}
|
||
|---|---|---|---|---|
| RESULTADO(S) | CÓDIGO(S) | INDICADOR(ES) | ESCALAS DE MEDICIÓN | PUNTUACIÓN DIANA Mantener Aumentar |
|
{obj.titulo}
Dominio: {objDominioNum}
Clase: {objClaseCode}
|
)}
{ind.codigo} | {ind.descripcion} | {i === 0 && (
{obj.escala.map((e, idx) => (
{e} ({idx + 1})
))}
|
)}
{sc.basal}
{sc.meta}
|
Elige una plataforma. Tu plan se envía como mensaje listo para enviar.