/** * Editor de Protocolo Completo * * Permite crear y editar protocolos operativos con: * - Pasos rápidos estructurados * - Checklist integrado * - Dosis inline * - Herramientas de contexto * - Fuentes clínicas * - Vista previa "modo TES" */ import { useState, useEffect } from 'react'; import { useParams, useNavigate } from 'react-router-dom'; import { Save, Plus, Trash2, GripVertical, AlertCircle, Eye, ArrowLeft, Pill, BookOpen, Calculator, CheckSquare, } from 'lucide-react'; import { contentService } from '../services/content'; import { useAuth } from '../contexts/AuthContext'; import ResourcesManager from '../components/content/ResourcesManager'; import ValidationHistory from '../components/content/ValidationHistory'; import type { Protocol, ProtocolStep, ProtocolChecklistItem, InlineDose, ProtocolContextTool, ClinicalSource, } from '../../shared/types/content'; export default function ProtocolEditorPage() { const { id } = useParams<{ id?: string }>(); const navigate = useNavigate(); const { hasPermission } = useAuth(); const isEdit = !!id; const [isLoading, setIsLoading] = useState(false); const [isSaving, setIsSaving] = useState(false); const [showPreview, setShowPreview] = useState(false); const [errors, setErrors] = useState>({}); const [activeTab, setActiveTab] = useState<'basic' | 'steps' | 'checklist' | 'doses' | 'tools' | 'sources' | 'resources'>('basic'); const [associatedResources, setAssociatedResources] = useState([]); const [showResourceSelector, setShowResourceSelector] = useState(false); // Estado del protocolo const [protocol, setProtocol] = useState>({ id: id || '', type: 'protocol', level: 'operativo', title: '', shortTitle: '', description: '', category: 'soporte_vital', subcategory: '', priority: 'critico', ageGroup: 'adulto', status: 'draft', content: { pasosRapidos: [], warnings: [], keyPoints: [], equipment: [], drugs: [], version: 1, lastUpdated: new Date().toISOString(), }, }); // Cargar protocolo existente useEffect(() => { if (isEdit && id) { setIsLoading(true); contentService .getById(id) .then((data: any) => { setProtocol({ ...data, content: data.content || { pasosRapidos: [], warnings: [], keyPoints: [], equipment: [], drugs: [], version: 1, lastUpdated: new Date().toISOString(), }, }); }) .catch((error) => { console.error('Error cargando protocolo:', error); setErrors({ general: 'Error al cargar el protocolo' }); }) .finally(() => setIsLoading(false)); } }, [id, isEdit]); // Validación const validate = (): boolean => { const newErrors: Record = {}; if (!protocol.id?.trim()) { newErrors.id = 'ID es requerido'; } if (!protocol.title?.trim()) { newErrors.title = 'Título es requerido'; } if (!protocol.content?.pasosRapidos || protocol.content.pasosRapidos.length === 0) { newErrors.pasosRapidos = 'Debe tener al menos un paso'; } setErrors(newErrors); return Object.keys(newErrors).length === 0; }; // Guardar const handleSave = async () => { if (!validate()) { return; } if (!hasPermission('content:write:protocol')) { setErrors({ general: 'No tienes permisos para editar protocolos' }); return; } setIsSaving(true); try { const payload = { id: protocol.id, type: 'protocol', level: 'operativo', title: protocol.title, shortTitle: protocol.shortTitle, description: protocol.description, category: protocol.category, subcategory: protocol.subcategory, priority: protocol.priority, ageGroup: protocol.ageGroup, content: protocol.content, status: protocol.status || 'draft', }; if (isEdit) { await contentService.update(id!, payload); } else { await contentService.create(payload); } navigate('/content'); } catch (error: any) { console.error('Error guardando protocolo:', error); setErrors({ general: error.response?.data?.error || 'Error al guardar' }); } finally { setIsSaving(false); } }; // Gestión de pasos rápidos const addStep = () => { const newStep: ProtocolStep = { order: (protocol.content?.pasosRapidos?.length || 0) + 1, text: '', critical: false, }; setProtocol({ ...protocol, content: { ...protocol.content!, pasosRapidos: [...(protocol.content?.pasosRapidos || []), newStep], }, }); }; const removeStep = (order: number) => { const steps = protocol.content?.pasosRapidos?.filter((s) => s.order !== order) || []; steps.forEach((step, index) => { step.order = index + 1; }); setProtocol({ ...protocol, content: { ...protocol.content!, pasosRapidos: steps, }, }); }; const updateStep = (order: number, updates: Partial) => { const steps = protocol.content?.pasosRapidos?.map((step) => step.order === order ? { ...step, ...updates } : step ) || []; setProtocol({ ...protocol, content: { ...protocol.content!, pasosRapidos: steps, }, }); }; // Gestión de warnings const addWarning = () => { const warnings = [...(protocol.content?.warnings || []), '']; setProtocol({ ...protocol, content: { ...protocol.content!, warnings, }, }); }; const updateWarning = (index: number, value: string) => { const warnings = [...(protocol.content?.warnings || [])]; warnings[index] = value; setProtocol({ ...protocol, content: { ...protocol.content!, warnings: warnings.filter((w) => w.trim()), }, }); }; // Gestión de dosis inline const addDose = () => { const newDose: InlineDose = { drugId: '', drugName: '', adultDose: '', route: 'IV', }; setProtocol({ ...protocol, content: { ...protocol.content!, dosisInline: [...(protocol.content?.dosisInline || []), newDose], }, }); }; const removeDose = (index: number) => { const doses = protocol.content?.dosisInline?.filter((_, i) => i !== index) || []; setProtocol({ ...protocol, content: { ...protocol.content!, dosisInline: doses, }, }); }; const updateDose = (index: number, updates: Partial) => { const doses = [...(protocol.content?.dosisInline || [])]; doses[index] = { ...doses[index], ...updates }; setProtocol({ ...protocol, content: { ...protocol.content!, dosisInline: doses, }, }); }; // Gestión de fuentes clínicas const addSource = () => { const newSource: ClinicalSource = { organization: '', guideline: '', year: new Date().getFullYear(), }; setProtocol({ ...protocol, content: { ...protocol.content!, fuentes: [...(protocol.content?.fuentes || []), newSource], }, }); }; const removeSource = (index: number) => { const sources = protocol.content?.fuentes?.filter((_, i) => i !== index) || []; setProtocol({ ...protocol, content: { ...protocol.content!, fuentes: sources, }, }); }; const updateSource = (index: number, updates: Partial) => { const sources = [...(protocol.content?.fuentes || [])]; sources[index] = { ...sources[index], ...updates }; setProtocol({ ...protocol, content: { ...protocol.content!, fuentes: sources, }, }); }; if (isLoading) { return (
Cargando protocolo...
); } return (
{/* Header */}

{isEdit ? 'Editar Protocolo' : 'Nuevo Protocolo'}

Crea y edita protocolos operativos con checklist, dosis y fuentes

{errors.general && (
{errors.general}
)}
{/* Formulario */}
{/* Tabs */}
{[ { id: 'basic', label: 'Básico' }, { id: 'steps', label: 'Pasos' }, { id: 'checklist', label: 'Checklist' }, { id: 'doses', label: 'Dosis' }, { id: 'tools', label: 'Herramientas' }, { id: 'resources', label: 'Recursos' }, { id: 'sources', label: 'Fuentes' }, ].map((tab) => ( ))}
{/* Tab: Básico */} {activeTab === 'basic' && (

Información Básica

setProtocol({ ...protocol, id: e.target.value })} disabled={isEdit} className="w-full px-4 py-2 bg-background border border-border rounded-lg focus:outline-none focus:ring-2 focus:ring-primary" placeholder="rcp-adulto-svb" /> {errors.id &&

{errors.id}

}
setProtocol({ ...protocol, title: e.target.value })} className="w-full px-4 py-2 bg-background border border-border rounded-lg focus:outline-none focus:ring-2 focus:ring-primary" placeholder="RCP Adulto - Soporte Vital Básico" /> {errors.title &&

{errors.title}

}
)} {/* Tab: Pasos */} {activeTab === 'steps' && (

Pasos Rápidos

{errors.pasosRapidos && (

{errors.pasosRapidos}

)}
{protocol.content?.pasosRapidos?.map((step) => (
#{step.order}