'use client'; import React, { useState, useRef, useEffect } from 'react'; import { useRouter } from 'next/navigation'; import ProtectedRoute from '@/components/shared/ProtectedRoute'; import { useAuth } from '@/hooks/useAuth'; import { useToast } from '@/hooks/use-toast'; import { listarPacientes } from '@/lib/api'; import { useReports } from '@/hooks/useReports'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { Textarea } from '@/components/ui/textarea'; import { Select, SelectTrigger, SelectContent, SelectItem, SelectValue } from '@/components/ui/select'; import { FileText, Upload, Settings, Eye, ArrowLeft } from 'lucide-react'; // Helpers para normalizar dados const getPatientName = (p: any) => p?.full_name ?? p?.nome ?? ''; const getPatientCpf = (p: any) => p?.cpf ?? ''; const getPatientSex = (p: any) => p?.sex ?? p?.sexo ?? ''; const getPatientAge = (p: any) => { if (!p) return ''; const bd = p?.birth_date ?? p?.data_nascimento ?? p?.birthDate; if (bd) { const d = new Date(bd); if (!isNaN(d.getTime())) { const age = Math.floor((Date.now() - d.getTime()) / (1000 * 60 * 60 * 24 * 365.25)); return `${age}`; } } return p?.idade ?? p?.age ?? ''; }; export default function LaudosEditorPage() { const router = useRouter(); const { user, token } = useAuth(); const { toast } = useToast(); const { createNewReport } = useReports(); // Estados principais const [pacienteSelecionado, setPacienteSelecionado] = useState(null); const [listaPacientes, setListaPacientes] = useState([]); const [content, setContent] = useState(''); const [activeTab, setActiveTab] = useState('editor'); const [showPreview, setShowPreview] = useState(false); // Estados para solicitante e prazo const [solicitanteId, setSolicitanteId] = useState(user?.id || ''); const displaySolicitante = user?.name || ''; const [prazoDate, setPrazoDate] = useState(''); const [prazoTime, setPrazoTime] = useState(''); // Campos do laudo const [campos, setCampos] = useState({ cid: '', diagnostico: '', conclusao: '', exame: '', especialidade: '', mostrarData: true, mostrarAssinatura: true, }); // Imagens const [imagens, setImagens] = useState([]); const [templates] = useState([ 'Exame normal, sem alterações significativas', 'Paciente em acompanhamento ambulatorial', 'Recomenda-se retorno em 30 dias', 'Alterações compatíveis com processo inflamatório', 'Resultado dentro dos parâmetros de normalidade', 'Recomendo seguimento com especialista', ]); // Histórico const [history, setHistory] = useState([]); const [historyIndex, setHistoryIndex] = useState(-1); // Carregar pacientes ao montar useEffect(() => { async function fetchPacientes() { try { if (!token) { setListaPacientes([]); return; } const pacientes = await listarPacientes(); setListaPacientes(pacientes || []); } catch (err) { console.warn('Erro ao carregar pacientes:', err); setListaPacientes([]); } } fetchPacientes(); }, [token]); // Atualizar histórico useEffect(() => { if (history[historyIndex] !== content) { const newHistory = history.slice(0, historyIndex + 1); setHistory([...newHistory, content]); setHistoryIndex(newHistory.length); } }, [content]); // Desfazer const handleUndo = () => { if (historyIndex > 0) { setContent(history[historyIndex - 1]); setHistoryIndex(historyIndex - 1); } }; // Formatação de texto const formatText = (type: string, value?: any) => { const textarea = document.querySelector('textarea') as HTMLTextAreaElement; if (!textarea) return; const start = textarea.selectionStart; const end = textarea.selectionEnd; const selectedText = textarea.value.substring(start, end); let formattedText = ''; switch (type) { case 'bold': formattedText = selectedText ? `**${selectedText}**` : '**texto em negrito**'; break; case 'italic': formattedText = selectedText ? `*${selectedText}*` : '*texto em itálico*'; break; case 'underline': formattedText = selectedText ? `__${selectedText}__` : '__texto sublinhado__'; break; case 'list-ul': formattedText = selectedText ? selectedText.split('\n').map((l) => `• ${l}`).join('\n') : '• item da lista'; break; case 'list-ol': formattedText = selectedText ? selectedText.split('\n').map((l, i) => `${i + 1}. ${l}`).join('\n') : '1. item da lista'; break; case 'indent': formattedText = selectedText ? selectedText.split('\n').map((l) => ` ${l}`).join('\n') : ' '; break; case 'outdent': formattedText = selectedText ? selectedText.split('\n').map((l) => l.replace(/^\s{1,4}/, '')).join('\n') : ''; break; case 'align-left': formattedText = selectedText ? `[left]${selectedText}[/left]` : '[left]Texto à esquerda[/left]'; break; case 'align-center': formattedText = selectedText ? `[center]${selectedText}[/center]` : '[center]Texto centralizado[/center]'; break; case 'align-right': formattedText = selectedText ? `[right]${selectedText}[/right]` : '[right]Texto à direita[/right]'; break; case 'align-justify': formattedText = selectedText ? `[justify]${selectedText}[/justify]` : '[justify]Texto justificado[/justify]'; break; case 'font-size': formattedText = selectedText ? `[size=${value}]${selectedText}[/size]` : `[size=${value}]Texto tamanho ${value}[/size]`; break; case 'font-family': formattedText = selectedText ? `[font=${value}]${selectedText}[/font]` : `[font=${value}]${value}[/font]`; break; case 'font-color': formattedText = selectedText ? `[color=${value}]${selectedText}[/color]` : `[color=${value}]${value}[/color]`; break; default: return; } const newText = textarea.value.substring(0, start) + formattedText + textarea.value.substring(end); setContent(newText); }; const insertTemplate = (template: string) => { setContent((prev: string) => (prev ? `${prev}\n\n${template}` : template)); }; const handleImageUpload = (e: React.ChangeEvent) => { const files = Array.from(e.target.files || []); files.forEach((file) => { const reader = new FileReader(); reader.onload = (e) => { setImagens((prev) => [ ...prev, { id: Date.now() + Math.random(), name: file.name, url: e.target?.result, type: file.type, }, ]); }; reader.readAsDataURL(file); }); }; const processContent = (content: string) => { return content .replace(/\*\*(.*?)\*\*/g, '$1') .replace(/\*(.*?)\*/g, '$1') .replace(/__(.*?)__/g, '$1') .replace(/\[left\]([\s\S]*?)\[\/left\]/g, '
$1
') .replace(/\[center\]([\s\S]*?)\[\/center\]/g, '
$1
') .replace(/\[right\]([\s\S]*?)\[\/right\]/g, '
$1
') .replace(/\[justify\]([\s\S]*?)\[\/justify\]/g, '
$1
') .replace(/\[size=(\d+)\]([\s\S]*?)\[\/size\]/g, '$2') .replace(/\[font=([^\]]+)\]([\s\S]*?)\[\/font\]/g, '$2') .replace(/\[color=([^\]]+)\]([\s\S]*?)\[\/color\]/g, '$2') .replace(/{{diagnostico}}/g, campos.diagnostico || '[DIAGNÓSTICO]') .replace(/{{conclusao}}/g, campos.conclusao || '[CONCLUSÃO]') .replace(/\n/g, '
'); }; const handleSave = async () => { try { if (!pacienteSelecionado?.id) { toast({ title: 'Erro', description: 'Selecione um paciente para continuar.', variant: 'destructive', }); return; } const userId = user?.id || '00000000-0000-0000-0000-000000000001'; let composedDueAt = undefined; if (prazoDate) { const t = prazoTime || '23:59'; composedDueAt = new Date(`${prazoDate}T${t}:00`).toISOString(); } const payload = { patient_id: pacienteSelecionado?.id, order_number: '', exam: campos.exame || '', diagnosis: campos.diagnostico || '', conclusion: campos.conclusao || '', cid_code: campos.cid || '', content_html: content, content_json: {}, requested_by: solicitanteId || userId, due_at: composedDueAt ?? new Date().toISOString(), hide_date: !campos.mostrarData, hide_signature: !campos.mostrarAssinatura, }; if (createNewReport) { await createNewReport(payload as any); toast({ title: 'Laudo criado com sucesso!', description: 'O laudo foi liberado e salvo.', variant: 'default', }); // Redirecionar para profissional router.push('/profissional'); } } catch (err) { toast({ title: 'Erro ao criar laudo', description: (err && typeof err === 'object' && 'message' in err) ? (err as any).message : String(err) || 'Tente novamente.', variant: 'destructive', }); } }; return (
{/* Header */}

Novo Laudo Médico

Crie um novo laudo selecionando um paciente

{/* Main Content */}
{/* Seleção de Paciente */}
{!pacienteSelecionado ? (
) : (
{getPatientName(pacienteSelecionado)}
{getPatientCpf(pacienteSelecionado) ? `CPF: ${getPatientCpf(pacienteSelecionado)} | ` : ''} {pacienteSelecionado?.birth_date ? `Nascimento: ${pacienteSelecionado.birth_date}` : getPatientAge(pacienteSelecionado) ? `Idade: ${getPatientAge(pacienteSelecionado)} anos` : ''} {getPatientSex(pacienteSelecionado) ? ` | Sexo: ${getPatientSex(pacienteSelecionado)}` : ''}
)} {/* Solicitante e Prazo */} {pacienteSelecionado && (
setPrazoDate(e.target.value)} className="text-xs sm:text-sm h-8 sm:h-10 flex-1" /> setPrazoTime(e.target.value)} className="text-xs sm:text-sm h-8 sm:h-10 flex-1" />

Defina a data e hora (opcional).

)}
{/* Tabs */}
{/* Content */}
{/* Left Panel */}
{/* Editor Tab */} {activeTab === 'editor' && (
{/* Toolbar */}
formatText('font-size', e.target.value)} className="w-10 sm:w-12 border rounded px-1 py-0.5 text-xs" title="Tamanho da fonte" /> formatText('font-color', e.target.value)} className="w-7 sm:w-8 h-7 sm:h-8 border rounded hidden sm:block" title="Cor da fonte" />
{templates.map((template, idx) => ( ))}
{/* Editor Textarea */}