diff --git a/susconecta/app/profissional/page.tsx b/susconecta/app/profissional/page.tsx index 789b9a5..5f1ffce 100644 --- a/susconecta/app/profissional/page.tsx +++ b/susconecta/app/profissional/page.tsx @@ -2,8 +2,6 @@ import React, { useState, useRef } from "react"; import SignatureCanvas from "react-signature-canvas"; -import ReactQuill from "react-quill"; -import "react-quill/dist/quill.snow.css"; import Link from "next/link"; import ProtectedRoute from "@/components/ProtectedRoute"; import { useAuth } from "@/hooks/useAuth"; @@ -28,7 +26,7 @@ import { SelectValue, } from "@/components/ui/select"; import { Avatar, AvatarImage, AvatarFallback } from "@/components/ui/avatar" -import { User, FolderOpen, X, Users, MessageSquare, ClipboardList, Plus, Edit, Trash2, ChevronLeft, ChevronRight, Clock, FileCheck, Upload, Download, Eye, History, Stethoscope, Pill, Activity } from "lucide-react" +import { User, FolderOpen, X, Users, MessageSquare, ClipboardList, Plus, Edit, Trash2, ChevronLeft, ChevronRight, Clock, FileCheck, Upload, Download, Eye, History, Stethoscope, Pill, Activity, Search } from "lucide-react" import { Calendar as CalendarIcon, FileText, Settings } from "lucide-react"; import { Tooltip, @@ -1652,257 +1650,1064 @@ const ProfissionalPage = () => { const renderLaudosSection = () => ( -
- -
- ); -// --- LaudoEditor COMPONENT --- -function LaudoEditor() { - - const pacientes = [ - { nome: "Ana Souza", cpf: "123.456.789-00", idade: 32, sexo: "Feminino" }, - { nome: "Bruno Lima", cpf: "987.654.321-00", idade: 45, sexo: "Masculino" }, - { nome: "Carla Menezes", cpf: "111.222.333-44", idade: 28, sexo: "Feminino" }, - ]; - - const [conteudo, setConteudo] = useState(""); - const [pacienteSelecionado, setPacienteSelecionado] = useState(null); - const [cid, setCid] = useState(""); - const [imagem, setImagem] = useState(null); - const [assinatura, setAssinatura] = useState(null); - const sigCanvasRef = useRef(null); - const [laudos, setLaudos] = useState([]); - const [preview, setPreview] = useState(false); - - - const handleSelectPaciente = (paciente: any) => { - setPacienteSelecionado(paciente); - }; - - const limparPaciente = () => setPacienteSelecionado(null); - - const salvarLaudo = (status: string) => { - if (!pacienteSelecionado) { - alert('Selecione um paciente.'); - return; - } - const novoLaudo = { - paciente: pacienteSelecionado.nome, - cpf: pacienteSelecionado.cpf, - idade: pacienteSelecionado.idade, - sexo: pacienteSelecionado.sexo, - cid, - conteudo, - imagem, - assinatura, - data: new Date().toLocaleString(), - status - }; - setLaudos(prev => [novoLaudo, ...prev]); - setPacienteSelecionado(null); setCid(""); setConteudo(""); setImagem(null); setAssinatura(null); - if (sigCanvasRef.current) sigCanvasRef.current.clear(); - }; - - return ( -
-
-
-

Laudo Médico

- - {!pacienteSelecionado ? ( -
-
- - - - -
-

Selecionar Paciente

-

Escolha um paciente para visualizar o prontuário completo

-
- -
- - - - -
-
-
- ) : ( -
-
-
{pacienteSelecionado.nome}
-
CPF: {pacienteSelecionado.cpf}
-
Idade: {pacienteSelecionado.idade}
-
Sexo: {pacienteSelecionado.sexo}
-
- -
- )} - -
-
- - setCid(e.target.value)} - placeholder="Ex: I10, E11, etc." - className="w-full p-2 border border-primary/20 rounded-md focus:ring-2 focus:ring-primary/30 focus:outline-none text-sm" - /> -
-
- - {!preview ? ( - - ) : ( -
-

Pré-visualização:

-
-
- )} -
-
- -
- { - const file = e.target.files?.[0]; - if (file) { - const reader = new FileReader(); - reader.onloadend = () => setImagem(reader.result as string); - reader.readAsDataURL(file); - } else { - setImagem(null); - } - }} - className="block w-full text-xs text-muted-foreground file:mr-2 file:py-1 file:px-2 file:rounded-md file:border-0 file:text-xs file:font-semibold file:bg-primary/10 file:text-primary hover:file:bg-primary/20" - /> - {imagem && ( - Pré-visualização - )} -
-
- -
- setAssinatura(sigCanvasRef.current?.isEmpty() ? null : sigCanvasRef.current?.toDataURL())} - /> -
- -
- {assinatura && ( - Assinatura - )} -
-
-
- - - -
- -
- -
-

Histórico de Laudos

- {laudos.length === 0 ? ( -

Nenhum laudo registrado.

- ) : ( - laudos.map((laudo: any, idx: number) => ( -
-
-
- - {laudo.paciente} -
- CPF: {laudo.cpf} -
-
-
Idade: {laudo.idade}
-
Sexo: {laudo.sexo}
-
Status: {laudo.status}
-
CID: {laudo.cid}
-
Data: {laudo.data}
-
- {laudo.assinatura && ( -
-

Assinatura Digital:

- Assinatura digital -
- )} - {laudo.imagem && ( -
-

Imagem:

- Imagem do laudo -
- )} -
-

Conteúdo:

-
-
-
- )) - )} -
-
+
+
); -} + + // --- NOVO SISTEMA DE LAUDOS COMPLETO --- + function LaudoManager() { + const [pacientesDisponiveis] = useState([ + { id: "95170038", nome: "Ana Souza", cpf: "123.456.789-00", idade: 42, sexo: "Feminino" }, + { id: "93203056", nome: "Bruno Lima", cpf: "987.654.321-00", idade: 33, sexo: "Masculino" }, + { id: "92953542", nome: "Carla Menezes", cpf: "111.222.333-44", idade: 67, sexo: "Feminino" }, + ]); + + const [laudos] = useState([ + { + id: "306494942", + data: "29/07/2025", + prazo: "29/07/2025", + paciente: { id: "95170038", nome: "Ana Souza", cpf: "123.456.789-00", idade: 42, sexo: "Feminino" }, + executante: "Carlos Andrade", + exame: "Ecocardiograma", + status: "Entregue", + urgente: true, + especialidade: "Cardiologia", + conteudo: `**ECOCARDIOGRAMA TRANSTORÁCICO** + +**Dados do Paciente:** +Nome: Ana Souza +Idade: 42 anos +Sexo: Feminino + +**Indicação Clínica:** +Investigação de sopro cardíaco + +**Técnica:** +Ecocardiograma transtorácico bidimensional com Doppler colorido e espectral. + +**Resultados:** +- Átrio esquerdo: dimensões normais +- Ventrículo esquerdo: função sistólica preservada, FEVE = 65% +- Valvas cardíacas: sem alterações significativas +- Pericárdio: sem derrame + +**Conclusão:** +Exame ecocardiográfico dentro dos limites da normalidade. + +**CID:** I25.9`, + cid: "I25.9", + diagnostico: "Exame ecocardiográfico normal", + conclusao: "Função cardíaca preservada, sem alterações estruturais significativas." + }, + { + id: "306463987", + data: "29/07/2025", + prazo: "29/07/2025", + paciente: { id: "93203056", nome: "Bruno Lima", cpf: "987.654.321-00", idade: 33, sexo: "Masculino" }, + executante: "Carlos Andrade", + exame: "Eletrocardiograma", + status: "Entregue", + urgente: true, + especialidade: "Cardiologia", + conteudo: `**ELETROCARDIOGRAMA DE REPOUSO** + +**Dados do Paciente:** +Nome: Bruno Lima +Idade: 33 anos +Sexo: Masculino + +**Indicação Clínica:** +Dor precordial atípica + +**Técnica:** +Eletrocardiograma de 12 derivações em repouso. + +**Resultados:** +- Ritmo: sinusal regular +- Frequência cardíaca: 72 bpm +- Eixo elétrico: normal +- Intervalos PR, QRS e QT: dentro dos limites normais +- Ondas Q patológicas: ausentes +- Alterações de ST-T: não observadas + +**Conclusão:** +Eletrocardiograma normal. + +**CID:** Z01.8`, + cid: "Z01.8", + diagnostico: "ECG normal", + conclusao: "Traçado eletrocardiográfico dentro dos parâmetros de normalidade." + }, + { + id: "306452545", + data: "29/07/2025", + prazo: "29/07/2025", + paciente: { id: "92953542", nome: "Carla Menezes", cpf: "111.222.333-44", idade: 67, sexo: "Feminino" }, + executante: "Carlos Andrade", + exame: "Dermatoscopia", + status: "Entregue", + urgente: true, + especialidade: "Dermatologia", + conteudo: `**DERMATOSCOPIA DIGITAL** + +**Dados do Paciente:** +Nome: Carla Menezes +Idade: 67 anos +Sexo: Feminino + +**Indicação Clínica:** +Avaliação de lesão pigmentada em dorso + +**Técnica:** +Dermatoscopia digital com magnificação de 10x e 20x. + +**Localização:** +Região dorsal, região escapular direita + +**Achados Dermatoscópicos:** +- Lesão melanocítica benigna +- Padrão reticular típico +- Bordas regulares e simétricas +- Pigmentação homogênea +- Ausência de estruturas atípicas + +**Conclusão:** +Nevo melanocítico benigno. Seguimento clínico recomendado. + +**CID:** D22.5`, + cid: "D22.5", + diagnostico: "Nevo melanocítico benigno", + conclusao: "Lesão benigna, recomenda-se acompanhamento dermatológico de rotina." + }, + ]); + + const [activeTab, setActiveTab] = useState("entregue"); + const [laudoSelecionado, setLaudoSelecionado] = useState(null); + const [isViewing, setIsViewing] = useState(false); + const [isCreatingNew, setIsCreatingNew] = useState(false); + + return ( +
+ {/* Header */} +
+
+
+

Gerenciamento de Laudo

+

Nesta seção você pode gerenciar todos os laudos gerados através da integração.

+
+ +
+
+ + {/* Tabs */} +
+
+ + + +
+ + {/* Filtros */} +
+
+
+ + + + +
+ +
+
+ + 01/07/2025 + - + 31/07/2025 +
+
+ +
+ + + +
+ + + + + + +
+
+ + {/* Tabela */} +
+ + + + Pedido + Data + Prazo + Paciente + Executante/Solicitante + Exame/Classificação + Ação + + + + {laudos.map((laudo) => ( + + +
+ {laudo.urgente && ( +
+ )} + {laudo.id} +
+
+ +
+
{laudo.data}
+
11:48
+
+
+ +
+
{laudo.prazo}
+
11:48
+
+
+ +
+
+ + {laudo.paciente.id} +
+
{laudo.paciente.nome}
+
+
+ {laudo.executante} + {laudo.exame || "-"} + + + +
+ ))} +
+
+
+
+ + {/* Visualizador de Laudo */} + {isViewing && laudoSelecionado && ( + setIsViewing(false)} /> + )} + + {/* Editor para Novo Laudo */} + {isCreatingNew && ( + setIsCreatingNew(false)} + isNewLaudo={true} + /> + )} +
+ ); + } + + // Visualizador de Laudo (somente leitura) + function LaudoViewer({ laudo, onClose }: { laudo: any; onClose: () => void }) { + return ( +
+
+ {/* Header */} +
+
+

Visualizar Laudo

+

+ Paciente: {laudo.paciente.nome} | Pedido: {laudo.id} | {laudo.especialidade} +

+
+ +
+ + {/* Content */} +
+
+ {/* Header do Laudo */} +
+

LAUDO MÉDICO - {laudo.especialidade.toUpperCase()}

+

+ Data: {laudo.data} +

+
+ + {/* Dados do Paciente */} +
+

Dados do Paciente:

+
+

Nome: {laudo.paciente.nome}

+

ID: {laudo.paciente.id}

+

CPF: {laudo.paciente.cpf}

+

Idade: {laudo.paciente.idade} anos

+

Sexo: {laudo.paciente.sexo}

+

CID: {laudo.cid}

+
+
+ + {/* Conteúdo do Laudo */} +
+
') + }} + /> +
+ + {/* Diagnóstico e Conclusão */} + {laudo.diagnostico && ( +
+

Diagnóstico:

+

{laudo.diagnostico}

+
+ )} + + {laudo.conclusao && ( +
+

Conclusão:

+

{laudo.conclusao}

+
+ )} + + {/* Assinatura */} +
+
+

Dr. Carlos Andrade

+

CRM 000000 - {laudo.especialidade}

+

Data: {laudo.data}

+
+
+
+ + {/* Footer */} +
+
+
+ Status: {laudo.status} | Executante: {laudo.executante} +
+ +
+
+
+
+ ); + } + + // Editor de Laudo Avançado (para novos laudos) + function LaudoEditor({ pacientes, laudo, onClose, isNewLaudo }: { pacientes?: any[]; laudo?: any; onClose: () => void; isNewLaudo?: boolean }) { + const [activeTab, setActiveTab] = useState("editor"); + const [content, setContent] = useState(""); + const [showPreview, setShowPreview] = useState(false); + const [pacienteSelecionado, setPacienteSelecionado] = useState(null); + const [campos, setCampos] = useState({ + cid: "", + diagnostico: "", + conclusao: "", + exame: "", + especialidade: "", + mostrarData: true, + mostrarAssinatura: true + }); + 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" + ]); + + const sigCanvasRef = useRef(null); + + const formatText = (type: string) => { + 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": + formattedText = selectedText ? `• ${selectedText}` : "• item da lista"; + break; + } + + const newText = textarea.value.substring(0, start) + formattedText + textarea.value.substring(end); + setContent(newText); + }; + + const insertTemplate = (template: string) => { + setContent(prev => prev ? `${prev}\n\n${template}` : template); + }; + + const insertCampo = (campo: string) => { + const placeholder = `{{${campo}}}`; + setContent(prev => prev ? `${prev} ${placeholder}` : placeholder); + }; + + 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) => { + const paciente = isNewLaudo ? pacienteSelecionado : laudo?.paciente; + return content + .replace(/\*\*(.*?)\*\*/g, '$1') + .replace(/\*(.*?)\*/g, '$1') + .replace(/(.*?)<\/u>/g, '$1') + .replace(/{{nome_paciente}}/g, paciente?.nome || '[NOME_PACIENTE]') + .replace(/{{idade_paciente}}/g, paciente?.idade?.toString() || '[IDADE]') + .replace(/{{sexo_paciente}}/g, paciente?.sexo || '[SEXO]') + .replace(/{{cid}}/g, campos.cid || '[CID]') + .replace(/{{diagnostico}}/g, campos.diagnostico || '[DIAGNÓSTICO]') + .replace(/{{conclusao}}/g, campos.conclusao || '[CONCLUSÃO]') + .replace(/\n/g, '
'); + }; + + return ( +
+
+ {/* Header */} +
+
+
+

+ {isNewLaudo ? "Novo Laudo Médico" : "Editor de Laudo"} +

+ {isNewLaudo ? ( +

+ Crie um novo laudo selecionando um paciente +

+ ) : ( +

+ Paciente: {laudo?.paciente?.nome} | Pedido: {laudo?.id} +

+ )} +
+ +
+ + {/* Seleção de Paciente (apenas para novos laudos) */} + {isNewLaudo && ( +
+ {!pacienteSelecionado ? ( +
+ + +
+ ) : ( +
+
+
{pacienteSelecionado.nome}
+
+ CPF: {pacienteSelecionado.cpf} | Idade: {pacienteSelecionado.idade} anos | Sexo: {pacienteSelecionado.sexo} +
+
+ +
+ )} +
+ )} +
+ + {/* Tabs */} +
+ {isNewLaudo && ( + + )} + + + + +
+ + {/* Content */} +
+ {/* Left Panel */} +
+ {activeTab === "info" && isNewLaudo && ( +
+ {!pacienteSelecionado ? ( +
+
+ +

Selecione um paciente primeiro

+
+
+ ) : ( +
+
+

Informações do Exame

+ +
+
+ + +
+ +
+ + setCampos(prev => ({ ...prev, exame: e.target.value }))} + placeholder="Ex: Ecocardiograma, Dermatoscopia, etc." + /> +
+
+
+ +
+

Dados do Paciente

+
+
+
+ Nome: {pacienteSelecionado.nome} +
+
+ ID: {pacienteSelecionado.id} +
+
+ CPF: {pacienteSelecionado.cpf} +
+
+ Idade: {pacienteSelecionado.idade} anos +
+
+ Sexo: {pacienteSelecionado.sexo} +
+
+
+
+ +
+ +
+
+ )} +
+ )} + + {activeTab === "editor" && ( +
+ {/* Toolbar */} +
+
+ + + + + +
+ + + + +
+ + {/* Templates */} +
+

Frases rápidas:

+
+ {templates.map((template, idx) => ( + + ))} +
+
+
+ + {/* Editor */} +
+