'use client'; import React, { useState, useRef, useEffect } from 'react'; import { useRouter, useParams } from 'next/navigation'; import ProtectedRoute from '@/components/shared/ProtectedRoute'; import { useAuth } from '@/hooks/useAuth'; import { useToast } from '@/hooks/use-toast'; import { buscarRelatorioPorId, buscarPacientePorId } 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 { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from '@/components/ui/dropdown-menu'; import { FileText, Settings, Eye, ArrowLeft, BookOpen, Upload } from 'lucide-react'; export default function EditarLaudoPage() { const router = useRouter(); const params = useParams(); const { user, token } = useAuth(); const { toast } = useToast(); const { updateExistingReport } = useReports(); const laudoId = params.id as string; // Estados principais const [reportData, setReportData] = useState(null); const [patient, setPatient] = useState(null); const [content, setContent] = useState(''); const [activeTab, setActiveTab] = useState('editor'); const [showPreview, setShowPreview] = useState(false); const [loading, setLoading] = useState(true); // Imagens const [imagens, setImagens] = useState([]); // Campos do laudo const [campos, setCampos] = useState({ cid: '', diagnostico: '', conclusao: '', exame: '', especialidade: '', mostrarData: true, mostrarAssinatura: true, }); // Editor ref const editorRef = useRef(null); // Frases prontas const frasesProntas = [ 'Paciente apresenta bom estado geral.', 'Recomenda-se seguimento clínico periódico.', 'Encaminhar para especialista.', 'Realizar novos exames em 30 dias.', 'Retorno em 15 dias para reavaliação.', 'Suspender medicamento em caso de efeitos colaterais.', 'Manter repouso relativo por 7 dias.', 'Seguir orientações prescritas rigorosamente.', 'Compatível com os achados clínicos.', 'Sem alterações significativas detectadas.', ]; // Estado para rastrear formatações ativas const [activeFormats, setActiveFormats] = useState({ bold: false, italic: false, underline: false, strikethrough: false, }); // Estado para rastrear alinhamento ativo const [activeAlignment, setActiveAlignment] = useState('left'); // Salvar conteúdo no localStorage sempre que muda useEffect(() => { if (content && laudoId) { localStorage.setItem(`laudo-draft-${laudoId}`, content); } }, [content, laudoId]); // Sincronizar conteúdo com o editor useEffect(() => { if (editorRef.current && content) { if (editorRef.current.innerHTML !== content) { editorRef.current.innerHTML = content; } } }, [content]); // Restaurar conteúdo quando volta para a aba editor useEffect(() => { if (activeTab === 'editor' && editorRef.current && content) { editorRef.current.focus(); const range = document.createRange(); const sel = window.getSelection(); range.setStart(editorRef.current, editorRef.current.childNodes.length); range.collapse(true); sel?.removeAllRanges(); sel?.addRange(range); } }, [activeTab]); // Atualizar formatações ativas ao mudar seleção useEffect(() => { const updateFormats = () => { setActiveFormats({ bold: document.queryCommandState('bold'), italic: document.queryCommandState('italic'), underline: document.queryCommandState('underline'), strikethrough: document.queryCommandState('strikeThrough'), }); // Detectar alinhamento ativo if (document.queryCommandState('justifyCenter')) { setActiveAlignment('center'); } else if (document.queryCommandState('justifyRight')) { setActiveAlignment('right'); } else if (document.queryCommandState('justifyFull')) { setActiveAlignment('justify'); } else { setActiveAlignment('left'); } }; editorRef.current?.addEventListener('mouseup', updateFormats); editorRef.current?.addEventListener('keyup', updateFormats); return () => { editorRef.current?.removeEventListener('mouseup', updateFormats); editorRef.current?.removeEventListener('keyup', updateFormats); }; }, []); // Carregar laudo ao montar useEffect(() => { async function fetchLaudo() { try { if (!laudoId || !token) { setLoading(false); return; } const report = await buscarRelatorioPorId(laudoId); setReportData(report); // Carregar paciente se existir patient_id const r = report as any; if (r.patient_id) { try { const patientData = await buscarPacientePorId(r.patient_id); setPatient(patientData); } catch (err) { console.warn('Erro ao carregar paciente:', err); } } // Preencher campos setCampos({ cid: r.cid_code || r.cid || '', diagnostico: r.diagnosis || r.diagnostico || '', conclusao: r.conclusion || r.conclusao || '', exame: r.exam || r.exame || '', especialidade: r.especialidade || '', mostrarData: !r.hide_date, mostrarAssinatura: !r.hide_signature, }); // Preencher conteúdo const contentHtml = r.content_html || r.conteudo_html || ''; // Verificar se existe rascunho salvo no localStorage const draftContent = typeof window !== 'undefined' ? localStorage.getItem(`laudo-draft-${laudoId}`) : null; const finalContent = draftContent || contentHtml; setContent(finalContent); if (editorRef.current) { editorRef.current.innerHTML = finalContent; // Colocar cursor no final do texto editorRef.current.focus(); const range = document.createRange(); const sel = window.getSelection(); range.setStart(editorRef.current, editorRef.current.childNodes.length); range.collapse(true); sel?.removeAllRanges(); sel?.addRange(range); } } catch (err) { console.warn('Erro ao carregar laudo:', err); toast({ title: 'Erro', description: 'Erro ao carregar o laudo.', variant: 'destructive', }); } finally { setLoading(false); } } fetchLaudo(); }, [laudoId, token, toast]); // Formatação com contenteditable const applyFormat = (command: string, value?: string) => { document.execCommand(command, false, value || undefined); editorRef.current?.focus(); }; const makeBold = () => applyFormat('bold'); const makeItalic = () => applyFormat('italic'); const makeUnderline = () => applyFormat('underline'); const makeStrikethrough = () => applyFormat('strikeThrough'); const insertUnorderedList = () => { document.execCommand('insertUnorderedList', false); editorRef.current?.focus(); }; const insertOrderedList = () => { document.execCommand('insertOrderedList', false); editorRef.current?.focus(); }; const alignText = (alignment: 'left' | 'center' | 'right' | 'justify') => { editorRef.current?.focus(); const alignCommands: { [key: string]: string } = { left: 'justifyLeft', center: 'justifyCenter', right: 'justifyRight', justify: 'justifyFull', }; document.execCommand(alignCommands[alignment], false, undefined); if (editorRef.current) { setContent(editorRef.current.innerHTML); } }; const alignLeft = () => alignText('left'); const alignCenter = () => alignText('center'); const alignRight = () => alignText('right'); const alignJustify = () => alignText('justify'); const insertFraseProta = (frase: string) => { editorRef.current?.focus(); document.execCommand('insertText', false, frase + ' '); if (editorRef.current) { setContent(editorRef.current.innerHTML); } }; 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 (!reportData?.id) { toast({ title: 'Erro', description: 'ID do laudo não encontrado.', variant: 'destructive', }); return; } // Pegar conteúdo diretamente do DOM para garantir que está atualizado const currentContent = editorRef.current?.innerHTML || content; const payload = { exam: campos.exame || '', diagnosis: campos.diagnostico || '', conclusion: campos.conclusao || '', cid_code: campos.cid || '', content_html: currentContent, content_json: {}, hide_date: !campos.mostrarData, hide_signature: !campos.mostrarAssinatura, imagens, }; if (updateExistingReport) { await updateExistingReport(reportData.id, payload as any); // Limpar rascunho do localStorage após salvar if (typeof window !== 'undefined') { localStorage.removeItem(`laudo-draft-${reportData.id}`); } toast({ title: 'Laudo atualizado com sucesso!', description: 'As alterações foram salvas.', variant: 'default', }); router.push(`/laudos/${reportData.id}`); } } catch (err) { toast({ title: 'Erro ao atualizar laudo', description: (err && typeof err === 'object' && 'message' in err) ? (err as any).message : String(err) || 'Tente novamente.', variant: 'destructive', }); } }; if (loading) { return (
Carregando laudo...
); } return (
{/* Header */}

Editar Laudo Médico

Atualize as informações do laudo

{patient && (

Paciente: {patient.full_name || patient.name || 'N/A'}

)}
{/* Main Content */}
{/* Tabs */}
{/* Content */}
{/* Left Panel */}
{/* Editor Tab */} {activeTab === 'editor' && (
{/* Toolbar */}
{/* Font Family */} {/* Font Size */}
{frasesProntas.map((frase, index) => ( insertFraseProta(frase)} className="text-xs cursor-pointer" > {frase} ))}
{/* Editor contenteditable */}
setContent(e.currentTarget.innerHTML)} onPaste={(e) => { e.preventDefault(); const text = e.clipboardData.getData('text/plain'); document.execCommand('insertText', false, text); }} className="w-full h-full overflow-auto p-3 text-sm border border-border rounded bg-background text-foreground outline-none empty:before:content-['Digite_aqui...'] empty:before:text-muted-foreground" style={{ caretColor: 'currentColor' }} suppressContentEditableWarning />
)} {/* Imagens Tab */} {activeTab === 'imagens' && (
{imagens.map((img) => (
{img.type.startsWith('image/') ? ( {img.name} ) : (
)}

{img.name}

))}
)} {/* Campos Tab */} {activeTab === 'campos' && (
setCampos((prev) => ({ ...prev, cid: e.target.value }))} placeholder="Ex: M25.5, I10, etc." className="text-xs sm:text-sm mt-1 h-8 sm:h-10" />
setCampos((prev) => ({ ...prev, exame: e.target.value }))} placeholder="Exame realizado" className="text-xs sm:text-sm mt-1 h-8 sm:h-10" />