-
Laudo Médico
+ const renderEditorLaudo = () => {
+ return (
+
+ {/* Cabeçalho do Editor */}
+
+
+
setModoEdicao(null)}
+ className="cursor-pointer"
+ >
+
+ Voltar
+
+
+
+ {modoEdicao === 'novo' ? 'Novo Laudo' : 'Editar Laudo'}
+
+
+ Este editor permite escrever relatórios de forma livre, com formatação de texto rica.
+
+
+
- {!pacienteSelecionado ? (
-
-
-
Selecionar Paciente
-
Escolha um paciente para visualizar o prontuário completo
-
-
Escolha o paciente:
-
-
{
- const p = pacientes.find(p => p.cpf === e.target.value);
- if (p) handleSelectPaciente(p);
- }}
- defaultValue=""
+
+ setModoEdicao(null)}
+ className="cursor-pointer"
+ >
+ Cancelar
+
+ {
+ setEditorLaudo(prev => ({ ...prev, status: "Rascunho" }));
+ alert("Laudo salvo como rascunho!");
+ }}
+ className="cursor-pointer"
+ >
+ Salvar Rascunho
+
+ {
+ setEditorLaudo(prev => ({ ...prev, status: "Entregue" }));
+ alert("Laudo liberado com sucesso!");
+ setModoEdicao(null);
+ }}
+ className="cursor-pointer"
+ >
+ Liberar Laudo
+
+
+
+
+ {/* Tabs do Editor */}
+
+
+
+ {[
+ { id: 'editor', nome: 'Editor', icone: FileText },
+ { id: 'imagens', nome: 'Imagens', icone: Upload },
+ { id: 'anexos', nome: 'Anexos PDF', icone: FileText },
+ { id: 'preview', nome: 'Pré-visualização', icone: Eye }
+ ].map((aba) => {
+ const Icone = aba.icone;
+ return (
+ setAbaAtiva(aba.id as any)}
+ className={`py-4 px-1 border-b-2 font-medium text-sm flex items-center gap-2 transition-colors cursor-pointer ${
+ abaAtiva === aba.id
+ ? 'border-primary text-primary'
+ : 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300'
+ }`}
>
- Selecione um paciente...
- {pacientes.map(p => (
- {p.nome} - {p.cpf}
- ))}
-
-
-
-
-
+
+ {aba.nome}
+
+ );
+ })}
+
+
+
+
+ {abaAtiva === 'editor' && renderAbaEditor()}
+ {abaAtiva === 'imagens' && renderAbaImagens()}
+ {abaAtiva === 'anexos' && renderAbaAnexos()}
+ {abaAtiva === 'preview' && renderAbaPreview()}
+
+
+
+ );
+ };
+
+ const renderAbaEditor = () => (
+
+ {/* Seleção de Paciente */}
+
+
+
Paciente *
+ {modoEdicao === 'editar' ? (
+ // Modo edição: mostrar dados fixos do paciente
+
+
+
+
{editorLaudo.pacienteNome || laudoAtivo?.recipient}
+
{editorLaudo.pacienteCpf}
) : (
-
-
-
{pacienteSelecionado.nome}
-
CPF: {pacienteSelecionado.cpf}
-
Idade: {pacienteSelecionado.idade}
-
Sexo: {pacienteSelecionado.sexo}
-
-
Trocar paciente
-
+ // Modo novo: mostrar select de pacientes
+
{
+ const pacienteSelecionado = pacientes.find(p => p.cpf === value);
+ if (pacienteSelecionado) {
+ setEditorLaudo(prev => ({
+ ...prev,
+ pacienteId: value,
+ pacienteNome: pacienteSelecionado.nome,
+ pacienteCpf: pacienteSelecionado.cpf,
+ pacienteIdade: pacienteSelecionado.idade.toString()
+ }));
+ }
+ }}
+ >
+
+
+
+
+ {pacientes.map((paciente) => (
+
+ {paciente.nome} - {paciente.cpf}
+
+ ))}
+
+
)}
-
-
-
-
Histórico de Laudos
- {laudos.length === 0 ? (
-
Nenhum laudo registrado.
- ) : (
- laudos.map((laudo: any, idx: number) => (
-
-
-
-
Idade: {laudo.idade}
-
Sexo: {laudo.sexo}
-
Status: {laudo.status}
-
CID: {laudo.cid}
-
Data: {laudo.data}
-
- {laudo.assinatura && (
-
-
Assinatura Digital:
-
-
- )}
- {laudo.imagem && (
-
-
Imagem:
-
-
- )}
-
-
- ))
- )}
+
+ CID *
+ setEditorLaudo(prev => ({ ...prev, cid: e.target.value }))}
+ />
+
+
+
+ {/* Campos Principais do Laudo */}
+
+
+ Diagnóstico *
+
+
+
+ Conclusão *
+
+
+
+ {/* Modelos e Frases */}
+
+
Modelos e Frases
+
+ {Object.entries(modelosTexto).map(([nome, texto]) => (
+ {
+ setEditorLaudo(prev => ({
+ ...prev,
+ conteudo: prev.conteudo + (prev.conteudo ? '\n\n' : '') + texto
+ }));
+ }}
+ className="text-left justify-start cursor-pointer"
+ >
+ {nome}
+
+ ))}
+
+
+
+ {/* Editor de Texto Rico */}
+
+ Conteúdo do Laudo *
+ setEditorLaudo(prev => ({ ...prev, conteudo: content }))}
+ />
+
+
+
+
+ {/* Assinatura Digital */}
+
+
Assinatura Digital
+
+
{
+ if (!sigCanvasLaudoRef.current?.isEmpty()) {
+ setAssinaturaLaudo(sigCanvasLaudoRef.current?.toDataURL());
+ }
+ }}
+ />
+
+ {
+ sigCanvasLaudoRef.current?.clear();
+ setAssinaturaLaudo(null);
+ }}
+ className="cursor-pointer"
+ >
+ Limpar
+
+
+
+
+
+ {/* Opções Finais */}
+
);
-}
+
+ const renderAbaImagens = () => (
+
+
+
Imagens do Laudo
+
+
+ Upload Imagem
+
+
+
+
+
+
Arraste imagens aqui ou clique para selecionar
+
Formatos aceitos: JPG, PNG, DICOM (máx. 10MB por arquivo)
+
{
+ // Lógica para upload de imagens
+ console.log("Imagens selecionadas:", e.target.files);
+ }}
+ />
+
+
+ {editorLaudo.imagens.length > 0 && (
+
+ {editorLaudo.imagens.map((img, index) => (
+
+
+
{
+ setEditorLaudo(prev => ({
+ ...prev,
+ imagens: prev.imagens.filter((_, i) => i !== index)
+ }));
+ }}
+ >
+
+
+
+ ))}
+
+ )}
+
+ );
+
+ const renderAbaAnexos = () => (
+
+
+
Anexos PDF
+
+
+ Importar PDF
+
+
+
+
+
+
Arraste PDFs aqui ou clique para selecionar
+
Anexar resultados de exames externos (máx. 25MB por arquivo)
+
+
+ {editorLaudo.pdfAnexos.length > 0 && (
+
+ {editorLaudo.pdfAnexos.map((pdf, index) => (
+
+
+
+
+
Documento_{index + 1}.pdf
+
2.4 MB
+
+
+
+
+
+
+ {
+ setEditorLaudo(prev => ({
+ ...prev,
+ pdfAnexos: prev.pdfAnexos.filter((_, i) => i !== index)
+ }));
+ }}
+ className="cursor-pointer"
+ >
+
+
+
+
+ ))}
+
+ )}
+
+ );
+
+ const renderAbaPreview = () => {
+ const pacienteSelecionado = pacientes.find(p => p.cpf === editorLaudo.pacienteId);
+
+ return (
+
+
+
Pré-visualização do Laudo
+
+
+ Gerar PDF
+
+
+
+
+ {/* Cabeçalho do Laudo */}
+
+
LAUDO MÉDICO
+ {editorLaudo.incluirData && (
+
Data: {new Date().toLocaleDateString('pt-BR')}
+ )}
+
+
+ {/* Dados do Paciente */}
+ {pacienteSelecionado && (
+
+
+ Paciente: {pacienteSelecionado.nome}
+
+
+ CPF: {pacienteSelecionado.cpf}
+
+
+ Idade: {pacienteSelecionado.idade} anos
+
+ {editorLaudo.cid && (
+
+ CID: {editorLaudo.cid}
+
+ )}
+
+ )}
+
+ {/* Conteúdo */}
+
+ {editorLaudo.conteudo ? (
+
+ ) : (
+
+
+
Nenhum conteúdo adicionado ainda.
+
Vá para a aba "Editor" para escrever o laudo.
+
+ )}
+
+
+ {/* Diagnóstico */}
+ {editorLaudo.diagnostico && (
+
+
DIAGNÓSTICO:
+
{editorLaudo.diagnostico}
+
+ )}
+
+ {/* Conclusão */}
+ {editorLaudo.conclusao && (
+
+
CONCLUSÃO:
+
{editorLaudo.conclusao}
+
+ )}
+
+ {/* Imagens */}
+ {editorLaudo.imagens.length > 0 && (
+
+
IMAGENS:
+
+ {editorLaudo.imagens.map((img, index) => (
+
+ ))}
+
+
+ )}
+
+ {/* Assinatura */}
+ {editorLaudo.incluirAssinatura && assinaturaLaudo && (
+
+
+
+
{medico.nome}
+
{medico.identificacao}
+
+
+ )}
+
+
+ );
+ };
const renderComunicacaoSection = () => (
@@ -2704,6 +3399,76 @@ function LaudoEditor() {
)}
+
+ {/* Modal de Visualização do Laudo */}
+
setLaudoVisualizacao(null)}>
+
+
+
+ {laudoVisualizacao?.exameTipo || "Laudo Médico"}
+
+
+
+ {laudoVisualizacao && (
+
+ {/* Cabeçalho do Laudo */}
+
+
LAUDO MÉDICO
+
Data: {laudoVisualizacao.data}
+
+
+ {/* Dados do Paciente */}
+
+
Paciente: {laudoVisualizacao.recipient}
+
Executante: {laudoVisualizacao.execSolicitante}
+
Exame: {laudoVisualizacao.exameTipo}
+ {laudoVisualizacao.cid && (
+
CID: {laudoVisualizacao.cid}
+ )}
+
+
+ {/* Diagnóstico */}
+ {laudoVisualizacao.diagnostico && (
+
+
DIAGNÓSTICO:
+
{laudoVisualizacao.diagnostico}
+
+ )}
+
+ {/* Conteúdo do Laudo */}
+ {laudoVisualizacao.conteudo && (
+
+ )}
+
+ {/* Conclusão */}
+ {laudoVisualizacao.conclusao && (
+
+
CONCLUSÃO:
+
{laudoVisualizacao.conclusao}
+
+ )}
+
+ {/* Rodapé */}
+
+
Status: {laudoVisualizacao.status}
+
+ {medico.nome}
+ {medico.identificacao}
+
+
+
+ )}
+
+
+
);
diff --git a/susconecta/components/dashboard/sidebar.tsx b/susconecta/components/dashboard/sidebar.tsx
index e7feeec..af90fff 100644
--- a/susconecta/components/dashboard/sidebar.tsx
+++ b/susconecta/components/dashboard/sidebar.tsx
@@ -24,7 +24,6 @@ import {
UserCheck,
FileText,
BarChart3,
- Settings,
Stethoscope,
User,
} from "lucide-react"
@@ -36,7 +35,6 @@ const navigation = [
{ name: "Médicos", href: "/doutores", icon: User },
{ name: "Consultas", href: "/consultas", icon: UserCheck },
{ name: "Relatórios", href: "/dashboard/relatorios", icon: BarChart3 },
- { name: "Configurações", href: "/configuracao", icon: Settings },
]
export function Sidebar() {
diff --git a/susconecta/next-env.d.ts b/susconecta/next-env.d.ts
index 40c3d68..830fb59 100644
--- a/susconecta/next-env.d.ts
+++ b/susconecta/next-env.d.ts
@@ -1,5 +1,6 @@
///
///
+///
// NOTE: This file should not be edited
-// see https://nextjs.org/docs/app/building-your-application/configuring/typescript for more information.
+// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
From 47967eb37f102548d4efb88b6418ba8845d16e39 Mon Sep 17 00:00:00 2001
From: Jonas Francisco
Date: Fri, 3 Oct 2025 02:22:40 -0300
Subject: [PATCH 02/19] feat: Implementa sistema completo de modo escuro
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- Adiciona ThemeProvider com next-themes para controle de tema
- Implementa componente SimpleThemeToggle com ícones sol/lua
- Configura CSS variables completas para light/dark modes no globals.css
- Padroniza todas as páginas de autenticação (login, login-admin, login-paciente)
- Padroniza todos os módulos principais (dashboard, pacientes, doutores, consultas, calendar, configuração)
- Padroniza completamente área profissional com todas as seções:
* Calendário e agendamentos
* Busca e gestão de pacientes
* Prontuários médicos completos
* Comunicação e relatórios
* Seções de exames (solicitados, resultados, diagnósticos, prescrições, evolução, anexos)
- Atualiza componentes UI (input, select, textarea) com bordas visíveis
- Implementa suporte dark mode em tooltips, badges de status e mensagens
- Garante acessibilidade e consistência visual em ambos os modos
- Mantém funcionalidades existentes sem breaking changes
Todos os elementos agora respondem adequadamente ao toggle de tema,
proporcionando experiência de usuário consistente e acessível.
---
.../app/(main-routes)/calendar/page.tsx | 6 +-
.../app/(main-routes)/configuracao/page.tsx | 8 +-
.../app/(main-routes)/consultas/page.tsx | 4 +-
.../app/(main-routes)/dashboard/page.tsx | 2 +-
.../app/(main-routes)/doutores/page.tsx | 4 +-
.../app/(main-routes)/pacientes/page.tsx | 4 +-
susconecta/app/globals.css | 35 ++
susconecta/app/layout.tsx | 11 +-
susconecta/app/login-admin/page.tsx | 10 +-
susconecta/app/login-paciente/page.tsx | 10 +-
susconecta/app/login/page.tsx | 10 +-
susconecta/app/profissional/page.tsx | 362 +++++++++---------
susconecta/components/header.tsx | 3 +
susconecta/components/simple-theme-toggle.tsx | 22 ++
susconecta/components/theme-toggle.tsx | 37 ++
susconecta/components/ui/input.tsx | 8 +-
susconecta/components/ui/select.tsx | 6 +-
susconecta/components/ui/textarea.tsx | 6 +-
18 files changed, 332 insertions(+), 216 deletions(-)
create mode 100644 susconecta/components/simple-theme-toggle.tsx
create mode 100644 susconecta/components/theme-toggle.tsx
diff --git a/susconecta/app/(main-routes)/calendar/page.tsx b/susconecta/app/(main-routes)/calendar/page.tsx
index dabcec7..e6e97ad 100644
--- a/susconecta/app/(main-routes)/calendar/page.tsx
+++ b/susconecta/app/(main-routes)/calendar/page.tsx
@@ -85,7 +85,7 @@ export default function AgendamentoPage() {
};
return (
-
+
@@ -122,7 +122,7 @@ export default function AgendamentoPage() {
setActiveTab("calendar")}
>
Calendário
@@ -130,7 +130,7 @@ export default function AgendamentoPage() {
setActiveTab("espera")}
>
Lista de espera
diff --git a/susconecta/app/(main-routes)/configuracao/page.tsx b/susconecta/app/(main-routes)/configuracao/page.tsx
index c88f8b6..3ab0e95 100644
--- a/susconecta/app/(main-routes)/configuracao/page.tsx
+++ b/susconecta/app/(main-routes)/configuracao/page.tsx
@@ -45,12 +45,12 @@ export default function ConfiguracaoPage() {
]
return (
-
+
{/* título */}
-
Configurações
+
Configurações
{/* introdução */}
-
+
Ajuste os principais parâmetros do sistema. Escolha uma das seções abaixo
para configurar horários, mensagens, notificações internas, permissões de usuários
e regras de segurança da clínica.
@@ -66,7 +66,7 @@ export default function ConfiguracaoPage() {
{item.title}
- {item.desc}
+ {item.desc}
diff --git a/susconecta/app/(main-routes)/consultas/page.tsx b/susconecta/app/(main-routes)/consultas/page.tsx
index c55cdac..47365d8 100644
--- a/susconecta/app/(main-routes)/consultas/page.tsx
+++ b/susconecta/app/(main-routes)/consultas/page.tsx
@@ -145,7 +145,7 @@ export default function ConsultasPage() {
if (showForm && editingAppointment) {
return (
-
+
@@ -162,7 +162,7 @@ export default function ConsultasPage() {
}
return (
-
+
Gerenciamento de Consultas
diff --git a/susconecta/app/(main-routes)/dashboard/page.tsx b/susconecta/app/(main-routes)/dashboard/page.tsx
index d4f8449..ffd17fe 100644
--- a/susconecta/app/(main-routes)/dashboard/page.tsx
+++ b/susconecta/app/(main-routes)/dashboard/page.tsx
@@ -1,7 +1,7 @@
export default function DashboardPage() {
return (
<>
-
+
Dashboard
diff --git a/susconecta/app/(main-routes)/doutores/page.tsx b/susconecta/app/(main-routes)/doutores/page.tsx
index 8fae66c..c034860 100644
--- a/susconecta/app/(main-routes)/doutores/page.tsx
+++ b/susconecta/app/(main-routes)/doutores/page.tsx
@@ -287,7 +287,7 @@ export default function DoutoresPage() {
if (showForm) {
return (
-
+
setShowForm(false)}>
@@ -307,7 +307,7 @@ export default function DoutoresPage() {
}
return (
-
+
Médicos
diff --git a/susconecta/app/(main-routes)/pacientes/page.tsx b/susconecta/app/(main-routes)/pacientes/page.tsx
index c9bffb7..9310a62 100644
--- a/susconecta/app/(main-routes)/pacientes/page.tsx
+++ b/susconecta/app/(main-routes)/pacientes/page.tsx
@@ -166,7 +166,7 @@ export default function PacientesPage() {
if (showForm) {
return (
-
+
setShowForm(false)}>
@@ -186,7 +186,7 @@ export default function PacientesPage() {
}
return (
-
+
Pacientes
diff --git a/susconecta/app/globals.css b/susconecta/app/globals.css
index 817775e..5234f59 100644
--- a/susconecta/app/globals.css
+++ b/susconecta/app/globals.css
@@ -39,6 +39,41 @@
--sidebar-ring: var(--color-blue-500);
}
+.dark {
+ --background: #0f172a;
+ --foreground: #cbd5e1;
+ --card: #1e293b;
+ --card-foreground: #e2e8f0;
+ --popover: #1e293b;
+ --popover-foreground: #cbd5e1;
+ --primary: var(--color-blue-500);
+ --primary-foreground: #ffffff;
+ --secondary: #334155;
+ --secondary-foreground: #cbd5e1;
+ --muted: #334155;
+ --muted-foreground: #94a3b8;
+ --accent: #0891b2;
+ --accent-foreground: #ffffff;
+ --destructive: #dc2626;
+ --destructive-foreground: #ffffff;
+ --border: #334155;
+ --input: #334155;
+ --ring: var(--color-blue-500);
+ --chart-1: #0891b2;
+ --chart-2: #0f766e;
+ --chart-3: #f59e0b;
+ --chart-4: #dc2626;
+ --chart-5: #94a3b8;
+ --sidebar: #1e293b;
+ --sidebar-foreground: #cbd5e1;
+ --sidebar-primary: var(--color-blue-500);
+ --sidebar-primary-foreground: #ffffff;
+ --sidebar-accent: var(--color-blue-500);
+ --sidebar-accent-foreground: #ffffff;
+ --sidebar-border: #334155;
+ --sidebar-ring: var(--color-blue-500);
+}
+
@theme inline {
--font-sans: var(--font-geist-sans);
--font-mono: var(--font-geist-mono);
diff --git a/susconecta/app/layout.tsx b/susconecta/app/layout.tsx
index f5b69fe..b7b71f1 100644
--- a/susconecta/app/layout.tsx
+++ b/susconecta/app/layout.tsx
@@ -1,6 +1,7 @@
import type React from "react"
import type { Metadata } from "next"
import { AuthProvider } from "@/hooks/useAuth"
+import { ThemeProvider } from "@/components/theme-provider"
import "./globals.css"
export const metadata: Metadata = {
@@ -17,11 +18,13 @@ export default function RootLayout({
children: React.ReactNode
}) {
return (
-
+
-
- {children}
-
+
+
+ {children}
+
+
)
diff --git a/susconecta/app/login-admin/page.tsx b/susconecta/app/login-admin/page.tsx
index fe3e41f..c4efa3c 100644
--- a/susconecta/app/login-admin/page.tsx
+++ b/susconecta/app/login-admin/page.tsx
@@ -45,13 +45,13 @@ export default function LoginAdminPage() {
}
return (
-
+
-
+
Login Administrador de Clínica
-
+
Entre com suas credenciais para acessar o sistema administrativo
@@ -63,7 +63,7 @@ export default function LoginAdminPage() {