2965 lines
121 KiB
TypeScript
2965 lines
121 KiB
TypeScript
"use client";
|
|
|
|
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";
|
|
import { buscarPacientes } from "@/lib/api";
|
|
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 {
|
|
Table,
|
|
TableBody,
|
|
TableCell,
|
|
TableHead,
|
|
TableHeader,
|
|
TableRow,
|
|
} from "@/components/ui/table";
|
|
import {
|
|
Select,
|
|
SelectContent,
|
|
SelectItem,
|
|
SelectTrigger,
|
|
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, Search } from "lucide-react"
|
|
import { Calendar as CalendarIcon, FileText, Settings } from "lucide-react";
|
|
import {
|
|
Dialog,
|
|
DialogContent,
|
|
DialogHeader,
|
|
DialogTitle,
|
|
} from "@/components/ui/dialog";
|
|
import {
|
|
Tooltip,
|
|
TooltipContent,
|
|
TooltipProvider,
|
|
TooltipTrigger,
|
|
} from "@/components/ui/tooltip";
|
|
|
|
import dayGridPlugin from "@fullcalendar/daygrid";
|
|
import timeGridPlugin from "@fullcalendar/timegrid";
|
|
import interactionPlugin from "@fullcalendar/interaction";
|
|
import ptBrLocale from "@fullcalendar/core/locales/pt-br";
|
|
|
|
const FullCalendar = dynamic(() => import("@fullcalendar/react"), {
|
|
ssr: false,
|
|
});
|
|
|
|
const pacientes = [
|
|
{ nome: "Ana Souza", cpf: "123.456.789-00", idade: 42, statusLaudo: "Finalizado" },
|
|
{ nome: "Bruno Lima", cpf: "987.654.321-00", idade: 33, statusLaudo: "Pendente" },
|
|
{ nome: "Carla Menezes", cpf: "111.222.333-44", idade: 67, statusLaudo: "Rascunho" },
|
|
];
|
|
|
|
const medico = {
|
|
nome: "Dr. Carlos Andrade",
|
|
identificacao: "CRM 000000 • Cardiologia e Dermatologia",
|
|
fotoUrl: "",
|
|
}
|
|
|
|
|
|
const colorsByType = {
|
|
Rotina: "#4dabf7",
|
|
Cardiologia: "#f76c6c",
|
|
Otorrino: "#f7b84d",
|
|
Pediatria: "#6cf78b",
|
|
Dermatologia: "#9b59b6",
|
|
Oftalmologia: "#2ecc71"
|
|
};
|
|
|
|
const ProfissionalPage = () => {
|
|
const { logout, user } = useAuth();
|
|
const [activeSection, setActiveSection] = useState('calendario');
|
|
const [pacienteSelecionado, setPacienteSelecionado] = useState<any>(null);
|
|
|
|
// Estados para o perfil do médico
|
|
const [isEditingProfile, setIsEditingProfile] = useState(false);
|
|
const [profileData, setProfileData] = useState({
|
|
nome: "Dr. Carlos Andrade",
|
|
email: user?.email || "carlos.andrade@hospital.com",
|
|
telefone: "(11) 99999-9999",
|
|
endereco: "Rua das Flores, 123 - Centro",
|
|
cidade: "São Paulo",
|
|
cep: "01234-567",
|
|
crm: "CRM 000000",
|
|
especialidade: "Cardiologia e Dermatologia",
|
|
biografia: "Médico especialista em cardiologia e dermatologia com mais de 15 anos de experiência em tratamentos clínicos e cirúrgicos."
|
|
});
|
|
|
|
// Estados para relatórios médicos
|
|
const [relatorioMedico, setRelatorioMedico] = useState({
|
|
pacienteNome: "",
|
|
pacienteCpf: "",
|
|
pacienteIdade: "",
|
|
profissionalNome: medico.nome,
|
|
profissionalCrm: medico.identificacao,
|
|
motivoRelatorio: "",
|
|
historicoClinico: "",
|
|
sinaisSintomas: "",
|
|
examesRealizados: "",
|
|
resultadosExames: "",
|
|
diagnosticos: "",
|
|
prognostico: "",
|
|
tratamentosRealizados: "",
|
|
recomendacoes: "",
|
|
dataRelatorio: new Date().toISOString().split('T')[0]
|
|
});
|
|
const [relatoriosMedicos, setRelatoriosMedicos] = useState<any[]>([]);
|
|
const [editandoRelatorio, setEditandoRelatorio] = useState<any>(null);
|
|
|
|
// Estados para funcionalidades do prontuário
|
|
const [consultasRegistradas, setConsultasRegistradas] = useState<any[]>([]);
|
|
const [historicoMedico, setHistoricoMedico] = useState<any[]>([]);
|
|
const [prescricoesMedicas, setPrescricoesMedicas] = useState<any[]>([]);
|
|
const [examesSolicitados, setExamesSolicitados] = useState<any[]>([]);
|
|
const [diagnosticos, setDiagnosticos] = useState<any[]>([]);
|
|
const [evolucaoQuadro, setEvolucaoQuadro] = useState<any[]>([]);
|
|
const [anexos, setAnexos] = useState<any[]>([]);
|
|
const [abaProntuarioAtiva, setAbaProntuarioAtiva] = useState('nova-consulta');
|
|
|
|
// Estados para campos principais da consulta
|
|
const [consultaAtual, setConsultaAtual] = useState({
|
|
dataConsulta: new Date().toISOString().split('T')[0],
|
|
anamnese: "",
|
|
exameFisico: "",
|
|
hipotesesDiagnosticas: "",
|
|
condutaMedica: "",
|
|
prescricoes: "",
|
|
retornoAgendado: "",
|
|
cid10: ""
|
|
});
|
|
|
|
const [events, setEvents] = useState<any[]>([
|
|
|
|
{
|
|
id: 1,
|
|
title: "Ana Souza",
|
|
type: "Cardiologia",
|
|
time: "09:00",
|
|
date: new Date().toISOString().split('T')[0],
|
|
pacienteId: "123.456.789-00",
|
|
color: colorsByType.Cardiologia
|
|
},
|
|
{
|
|
id: 2,
|
|
title: "Bruno Lima",
|
|
type: "Cardiologia",
|
|
time: "10:30",
|
|
date: new Date().toISOString().split('T')[0],
|
|
pacienteId: "987.654.321-00",
|
|
color: colorsByType.Cardiologia
|
|
},
|
|
{
|
|
id: 3,
|
|
title: "Carla Menezes",
|
|
type: "Dermatologia",
|
|
time: "14:00",
|
|
date: new Date().toISOString().split('T')[0],
|
|
pacienteId: "111.222.333-44",
|
|
color: colorsByType.Dermatologia
|
|
}
|
|
]);
|
|
const [editingEvent, setEditingEvent] = useState<any>(null);
|
|
const [showPopup, setShowPopup] = useState(false);
|
|
const [showActionModal, setShowActionModal] = useState(false);
|
|
const [step, setStep] = useState(1);
|
|
const [newEvent, setNewEvent] = useState({
|
|
title: "",
|
|
type: "",
|
|
time: "",
|
|
pacienteId: ""
|
|
});
|
|
const [selectedDate, setSelectedDate] = useState<string | null>(null);
|
|
const [selectedEvent, setSelectedEvent] = useState<any>(null);
|
|
const [currentCalendarDate, setCurrentCalendarDate] = useState(new Date());
|
|
|
|
// Estados para o gerenciamento de laudos
|
|
const [laudosData, setLaudosData] = useState<any[]>([
|
|
{
|
|
id: "30648642",
|
|
data: "23/07/2025",
|
|
preco: "25/07/2025",
|
|
recipient: "Ana Souza",
|
|
execSolicitante: "Dr. Carlos Andrade",
|
|
exameTipo: "Avaliação Cardiológica Completa",
|
|
status: "Entregue",
|
|
cid: "I25.1",
|
|
diagnostico: "Doença aterosclerótica do coração com angina de peito",
|
|
conclusao: "Paciente apresenta quadro de doença arterial coronariana estável. Recomendado tratamento medicamentoso e acompanhamento regular.",
|
|
conteudo: `**HISTÓRIA CLÍNICA:**
|
|
Paciente do sexo feminino, 42 anos, procurou atendimento devido a episódios de dor torácica aos esforços, com duração de aproximadamente 5 minutos, que melhora com repouso. Relata também dispneia aos médios esforços e palpitações ocasionais.
|
|
|
|
**ANTECEDENTES:**
|
|
- Hipertensão arterial sistêmica há 8 anos
|
|
- Dislipidemia
|
|
- Histórico familiar de doença arterial coronariana (pai)
|
|
- Sedentarismo
|
|
|
|
**EXAME FÍSICO:**
|
|
- PA: 145/90 mmHg
|
|
- FC: 76 bpm
|
|
- Peso: 68 kg, Altura: 1,62 m, IMC: 25,9 kg/m²
|
|
- Ausculta cardíaca: bulhas normofonéticas, sem sopros
|
|
- Ausculta pulmonar: murmúrio vesicular presente bilateralmente
|
|
|
|
**EXAMES COMPLEMENTARES:**
|
|
- ECG: ritmo sinusal, sem alterações isquêmicas
|
|
- Ecocardiograma: função sistólica preservada (FEVE: 65%), sem alterações segmentares
|
|
- Teste ergométrico: positivo para isquemia miocárdica
|
|
|
|
**CONDUTA:**
|
|
- Otimização do tratamento anti-hipertensivo
|
|
- Estatina para controle da dislipidemia
|
|
- Antiagregante plaquetário
|
|
- Orientações sobre estilo de vida
|
|
- Retorno em 30 dias`
|
|
},
|
|
{
|
|
id: "30645947",
|
|
data: "24/07/2025",
|
|
preco: "25/07/2025",
|
|
recipient: "Bruno Lima",
|
|
execSolicitante: "Dr. Carlos Andrade",
|
|
exameTipo: "Avaliação Dermatológica - Lesões Pigmentadas",
|
|
status: "Entregue",
|
|
cid: "D22.9",
|
|
diagnostico: "Nevo melanocítico benigno",
|
|
conclusao: "Lesões pigmentadas benignas. Recomendado acompanhamento dermatológico anual e uso de protetor solar.",
|
|
conteudo: `**HISTÓRIA CLÍNICA:**
|
|
Paciente masculino, 33 anos, comparece para avaliação de múltiplas lesões pigmentadas pelo corpo, algumas com crescimento recente. Nega sintomas como prurido, sangramento ou mudanças de coloração. Histórico de exposição solar intensa durante a infância e adolescência.
|
|
|
|
**ANTECEDENTES:**
|
|
- Fototipo II (pele clara, queima facilmente)
|
|
- Múltiplas queimaduras solares na infância
|
|
- Histórico familiar negativo para melanoma
|
|
- Uso irregular de protetor solar
|
|
|
|
**EXAME FÍSICO:**
|
|
**Dermatoscopia realizada em 12 lesões:**
|
|
|
|
*Lesão dorso (2cm superior ao ombro direito):*
|
|
- Mácula acastanhada, 4mm, bordas regulares
|
|
- Padrão dermatoscópico: rede pigmentar homogênea
|
|
- Score ABCD: 2,5 (baixo risco)
|
|
|
|
*Lesão região escapular esquerda:*
|
|
- Pápula pigmentada, 3mm, simétrica
|
|
- Padrão globular homogêneo
|
|
- Sem sinais de malignidade
|
|
|
|
*Demais lesões:*
|
|
- Características benignas similares
|
|
- Ausência de critérios de malignidade
|
|
- Padrões dermatoscópicos típicos de nevos
|
|
|
|
**CONDUTA:**
|
|
- Mapeamento corporal documentado
|
|
- Fotoproteção rigorosa (FPS 60+)
|
|
- Autoexame mensal orientado
|
|
- Reavaliação dermatoscópica em 12 meses
|
|
- Biópsia desnecessária no momento atual`
|
|
},
|
|
{
|
|
id: "30649123",
|
|
data: "25/07/2025",
|
|
preco: "26/07/2025",
|
|
recipient: "Carla Menezes",
|
|
execSolicitante: "Dr. Carlos Andrade",
|
|
exameTipo: "Avaliação Cardiológica - Insuficiência Cardíaca",
|
|
status: "Rascunho",
|
|
cid: "I50.9",
|
|
diagnostico: "Insuficiência cardíaca não especificada",
|
|
conclusao: "Insuficiência cardíaca classe funcional II. Necessário ajuste da medicação e monitorização rigorosa.",
|
|
conteudo: `**HISTÓRIA CLÍNICA:**
|
|
Paciente feminina, 67 anos, com queixa de dispneia progressiva aos esforços há 6 meses, associada a edema de membros inferiores e fadiga. Nega dor torácica, palpitações ou síncope.
|
|
|
|
**ANTECEDENTES:**
|
|
- Hipertensão arterial há 15 anos
|
|
- Diabetes mellitus tipo 2 há 10 anos
|
|
- Infarto agudo do miocárdio há 3 anos
|
|
- Tabagismo pregresso (parou há 5 anos)
|
|
|
|
**EXAME FÍSICO:**
|
|
- PA: 130/80 mmHg
|
|
- FC: 88 bpm (irregular)
|
|
- Edema ++/4+ em MMII
|
|
- Estase jugular a 45°
|
|
- Ausculta cardíaca: B3 audível, sopro sistólico 2+/6+
|
|
- Crepitações bibasais
|
|
|
|
**EXAMES:**
|
|
- BNP: 850 pg/ml (elevado)
|
|
- Ecocardiograma: FEVE 35%, dilatação de VE
|
|
- RX tórax: cardiomegalia, congestão pulmonar
|
|
|
|
**MEDICAÇÕES EM USO:**
|
|
- Enalapril 10mg 2x/dia
|
|
- Carvedilol 6,25mg 2x/dia
|
|
- Furosemida 40mg/dia
|
|
- Metformina 850mg 2x/dia
|
|
|
|
**CONDUTA PROPOSTA:**
|
|
- Ajuste das medicações para IC
|
|
- Restrição de sódio (<2g/dia)
|
|
- Controle rigoroso de peso diário
|
|
- Retorno em 15 dias`
|
|
}
|
|
]);
|
|
|
|
const [filtroStatus, setFiltroStatus] = useState("todos");
|
|
const [buscarTexto, setBuscarTexto] = useState("");
|
|
const [dataInicio, setDataInicio] = useState("");
|
|
const [dataFim, setDataFim] = useState("");
|
|
const [laudoAtivo, setLaudoAtivo] = useState<any>(null);
|
|
const [modoEdicao, setModoEdicao] = useState<'novo' | 'editar' | null>(null);
|
|
|
|
// Estados para o editor de laudo
|
|
const [editorLaudo, setEditorLaudo] = useState({
|
|
pacienteId: "",
|
|
pacienteNome: "",
|
|
pacienteCpf: "",
|
|
pacienteIdade: "",
|
|
cid: "",
|
|
diagnostico: "",
|
|
conclusao: "",
|
|
conteudo: "",
|
|
imagens: [] as string[],
|
|
pdfAnexos: [] as string[],
|
|
incluirData: true,
|
|
incluirAssinatura: true,
|
|
status: "Rascunho"
|
|
});
|
|
const [assinaturaLaudo, setAssinaturaLaudo] = useState<string | null>(null);
|
|
const [previewLaudo, setPreviewLaudo] = useState(false);
|
|
const [abaAtiva, setAbaAtiva] = useState<'editor' | 'imagens' | 'anexos' | 'preview'>('editor');
|
|
const [laudoVisualizacao, setLaudoVisualizacao] = useState<any>(null);
|
|
const sigCanvasLaudoRef = useRef<any>(null);
|
|
|
|
// Modelos e frases prontas
|
|
const modelosTexto = {
|
|
"Exame Normal": "O exame realizado apresentou parâmetros dentro da normalidade, sem alterações significativas detectadas.",
|
|
"Acompanhamento": "Recomenda-se acompanhamento médico regular para monitoramento da evolução do quadro clínico.",
|
|
"Alterações Leves": "Foram observadas alterações leves que requerem acompanhamento, sem indicação de intervenção imediata.",
|
|
"Urgente": "Os achados indicam necessidade de avaliação médica urgente e início de tratamento adequado."
|
|
};
|
|
|
|
const camposDinamicos = [
|
|
{ label: "Nome do Paciente", value: "{NOME_PACIENTE}" },
|
|
{ label: "Idade", value: "{IDADE}" },
|
|
{ label: "CPF", value: "{CPF}" },
|
|
{ label: "Data Atual", value: "{DATA_ATUAL}" },
|
|
{ label: "CID", value: "{CID}" },
|
|
{ label: "Médico", value: "{MEDICO}" }
|
|
];
|
|
|
|
const handleSave = (event: React.MouseEvent<HTMLButtonElement>) => {
|
|
event.preventDefault();
|
|
console.log("Laudo salvo!");
|
|
window.scrollTo({ top: 0, behavior: "smooth" });
|
|
};
|
|
|
|
const handleAbrirProntuario = (paciente: any) => {
|
|
setPacienteSelecionado(paciente);
|
|
|
|
const pacienteLaudo = document.getElementById('pacienteLaudo') as HTMLInputElement;
|
|
if (pacienteLaudo) pacienteLaudo.value = paciente.nome;
|
|
|
|
const destinatario = document.getElementById('destinatario') as HTMLInputElement;
|
|
if (destinatario) destinatario.value = `${paciente.nome} - ${paciente.cpf}`;
|
|
|
|
const prontuarioSection = document.getElementById('prontuario-paciente');
|
|
if (prontuarioSection) {
|
|
prontuarioSection.scrollIntoView({ behavior: 'smooth' });
|
|
}
|
|
};
|
|
|
|
const handleFecharProntuario = () => {
|
|
setPacienteSelecionado(null);
|
|
};
|
|
|
|
|
|
const navigateDate = (direction: 'prev' | 'next') => {
|
|
const newDate = new Date(currentCalendarDate);
|
|
newDate.setDate(newDate.getDate() + (direction === 'next' ? 1 : -1));
|
|
setCurrentCalendarDate(newDate);
|
|
};
|
|
|
|
const goToToday = () => {
|
|
setCurrentCalendarDate(new Date());
|
|
};
|
|
|
|
const formatDate = (date: Date) => {
|
|
return date.toLocaleDateString('pt-BR', {
|
|
weekday: 'long',
|
|
day: 'numeric',
|
|
month: 'long',
|
|
year: 'numeric'
|
|
});
|
|
};
|
|
|
|
// Filtrar eventos do dia atual
|
|
const getTodayEvents = () => {
|
|
const today = currentCalendarDate.toISOString().split('T')[0];
|
|
return events
|
|
.filter(event => event.date === today)
|
|
.sort((a, b) => a.time.localeCompare(b.time));
|
|
};
|
|
|
|
const getStatusColor = (type: string) => {
|
|
return colorsByType[type as keyof typeof colorsByType] || "#4dabf7";
|
|
};
|
|
|
|
// Funções para o perfil
|
|
const handleProfileChange = (field: string, value: string) => {
|
|
setProfileData(prev => ({
|
|
...prev,
|
|
[field]: value
|
|
}));
|
|
};
|
|
|
|
const handleSaveProfile = () => {
|
|
setIsEditingProfile(false);
|
|
alert('Perfil atualizado com sucesso!');
|
|
};
|
|
|
|
const handleCancelEdit = () => {
|
|
setIsEditingProfile(false);
|
|
};
|
|
|
|
// Funções para relatórios médicos
|
|
const handleRelatorioChange = (field: string, value: string) => {
|
|
setRelatorioMedico(prev => ({
|
|
...prev,
|
|
[field]: value
|
|
}));
|
|
};
|
|
|
|
const handleSalvarRelatorio = () => {
|
|
if (!relatorioMedico.pacienteNome || !relatorioMedico.motivoRelatorio) {
|
|
alert('Por favor, preencha pelo menos o nome do paciente e o motivo do relatório.');
|
|
return;
|
|
}
|
|
|
|
const novoRelatorio = {
|
|
...relatorioMedico,
|
|
id: Date.now(),
|
|
dataGeracao: new Date().toLocaleString()
|
|
};
|
|
|
|
if (editandoRelatorio) {
|
|
setRelatoriosMedicos(prev =>
|
|
prev.map(rel => rel.id === editandoRelatorio.id ? novoRelatorio : rel)
|
|
);
|
|
setEditandoRelatorio(null);
|
|
alert('Relatório médico atualizado com sucesso!');
|
|
} else {
|
|
setRelatoriosMedicos(prev => [novoRelatorio, ...prev]);
|
|
alert('Relatório médico salvo com sucesso!');
|
|
}
|
|
|
|
// Limpar formulário
|
|
setRelatorioMedico({
|
|
pacienteNome: "",
|
|
pacienteCpf: "",
|
|
pacienteIdade: "",
|
|
profissionalNome: medico.nome,
|
|
profissionalCrm: medico.identificacao,
|
|
motivoRelatorio: "",
|
|
historicoClinico: "",
|
|
sinaisSintomas: "",
|
|
examesRealizados: "",
|
|
resultadosExames: "",
|
|
diagnosticos: "",
|
|
prognostico: "",
|
|
tratamentosRealizados: "",
|
|
recomendacoes: "",
|
|
dataRelatorio: new Date().toISOString().split('T')[0]
|
|
});
|
|
};
|
|
|
|
const handleEditarRelatorio = (relatorio: any) => {
|
|
setRelatorioMedico(relatorio);
|
|
setEditandoRelatorio(relatorio);
|
|
};
|
|
|
|
const handleExcluirRelatorio = (id: number) => {
|
|
if (confirm('Tem certeza que deseja excluir este relatório médico?')) {
|
|
setRelatoriosMedicos(prev => prev.filter(rel => rel.id !== id));
|
|
alert('Relatório médico excluído com sucesso!');
|
|
}
|
|
};
|
|
|
|
const handleCancelarEdicaoRelatorio = () => {
|
|
setEditandoRelatorio(null);
|
|
setRelatorioMedico({
|
|
pacienteNome: "",
|
|
pacienteCpf: "",
|
|
pacienteIdade: "",
|
|
profissionalNome: medico.nome,
|
|
profissionalCrm: medico.identificacao,
|
|
motivoRelatorio: "",
|
|
historicoClinico: "",
|
|
sinaisSintomas: "",
|
|
examesRealizados: "",
|
|
resultadosExames: "",
|
|
diagnosticos: "",
|
|
prognostico: "",
|
|
tratamentosRealizados: "",
|
|
recomendacoes: "",
|
|
dataRelatorio: new Date().toISOString().split('T')[0]
|
|
});
|
|
};
|
|
|
|
|
|
const handleDateClick = (arg: any) => {
|
|
setSelectedDate(arg.dateStr);
|
|
setNewEvent({ title: "", type: "", time: "", pacienteId: "" });
|
|
setStep(1);
|
|
setEditingEvent(null);
|
|
setShowPopup(true);
|
|
};
|
|
|
|
|
|
const handleAddEvent = () => {
|
|
const paciente = pacientes.find(p => p.nome === newEvent.title);
|
|
const eventToAdd = {
|
|
id: Date.now(),
|
|
title: newEvent.title,
|
|
type: newEvent.type,
|
|
time: newEvent.time,
|
|
date: selectedDate || currentCalendarDate.toISOString().split('T')[0],
|
|
pacienteId: paciente ? paciente.cpf : "",
|
|
color: colorsByType[newEvent.type as keyof typeof colorsByType] || "#4dabf7"
|
|
};
|
|
setEvents((prev) => [...prev, eventToAdd]);
|
|
setShowPopup(false);
|
|
};
|
|
|
|
|
|
const handleEditEvent = () => {
|
|
setEvents((prevEvents) =>
|
|
prevEvents.map((ev) =>
|
|
ev.id.toString() === editingEvent.id.toString()
|
|
? {
|
|
...ev,
|
|
title: newEvent.title,
|
|
type: newEvent.type,
|
|
time: newEvent.time,
|
|
color: colorsByType[newEvent.type as keyof typeof colorsByType] || "#4dabf7"
|
|
}
|
|
: ev
|
|
)
|
|
);
|
|
setEditingEvent(null);
|
|
setShowPopup(false);
|
|
setShowActionModal(false);
|
|
};
|
|
|
|
|
|
const handleNextStep = () => {
|
|
if (step < 3) setStep(step + 1);
|
|
else editingEvent ? handleEditEvent() : handleAddEvent();
|
|
};
|
|
|
|
|
|
const handleEventClick = (clickInfo: any) => {
|
|
setSelectedEvent(clickInfo.event);
|
|
setShowActionModal(true);
|
|
};
|
|
|
|
|
|
const handleDeleteEvent = () => {
|
|
if (!selectedEvent) return;
|
|
setEvents((prevEvents) =>
|
|
prevEvents.filter((ev: any) => ev.id.toString() !== selectedEvent.id.toString())
|
|
);
|
|
setShowActionModal(false);
|
|
};
|
|
|
|
|
|
const handleStartEdit = () => {
|
|
if (!selectedEvent) return;
|
|
setEditingEvent(selectedEvent);
|
|
setNewEvent({
|
|
title: selectedEvent.title,
|
|
type: selectedEvent.extendedProps.type,
|
|
time: selectedEvent.extendedProps.time,
|
|
pacienteId: selectedEvent.extendedProps.pacienteId || ""
|
|
});
|
|
setStep(1);
|
|
setShowActionModal(false);
|
|
setShowPopup(true);
|
|
};
|
|
|
|
|
|
const renderEventContent = (eventInfo: any) => {
|
|
const bg = eventInfo.event.backgroundColor || eventInfo.event.extendedProps?.color || "#4dabf7";
|
|
|
|
return (
|
|
<div
|
|
className="flex items-center gap-1 text-xs p-1 rounded cursor-pointer"
|
|
style={{
|
|
backgroundColor: bg,
|
|
color: "#fff",
|
|
maxWidth: "100%",
|
|
whiteSpace: "nowrap",
|
|
overflow: "hidden",
|
|
textOverflow: "ellipsis"
|
|
}}
|
|
title={`${eventInfo.event.title} • ${eventInfo.event.extendedProps.type} • ${eventInfo.event.extendedProps.time}`}
|
|
>
|
|
<span className="truncate">{eventInfo.event.title}</span>
|
|
<span>•</span>
|
|
<span className="truncate">{eventInfo.event.extendedProps.type}</span>
|
|
<span>•</span>
|
|
<span>{eventInfo.event.extendedProps.time}</span>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
|
|
const renderCalendarioSection = () => {
|
|
const todayEvents = getTodayEvents();
|
|
|
|
return (
|
|
<section className="bg-card shadow-md rounded-lg p-6">
|
|
<div className="flex justify-between items-center mb-4">
|
|
<h2 className="text-2xl font-bold">Agenda do Dia</h2>
|
|
</div>
|
|
|
|
{/* Navegação de Data */}
|
|
<div className="flex items-center justify-between mb-6 p-4 bg-muted rounded-lg">
|
|
<div className="flex items-center space-x-4">
|
|
<Button
|
|
variant="outline"
|
|
size="sm"
|
|
onClick={() => navigateDate('prev')}
|
|
className="p-2 hover:bg-primary hover:text-primary-foreground cursor-pointer"
|
|
>
|
|
<ChevronLeft className="h-4 w-4" />
|
|
</Button>
|
|
<h3 className="text-lg font-medium text-foreground">
|
|
{formatDate(currentCalendarDate)}
|
|
</h3>
|
|
<Button
|
|
variant="outline"
|
|
size="sm"
|
|
onClick={() => navigateDate('next')}
|
|
className="p-2 hover:bg-primary hover:text-primary-foreground cursor-pointer"
|
|
>
|
|
<ChevronRight className="h-4 w-4" />
|
|
</Button>
|
|
<Button
|
|
variant="outline"
|
|
size="sm"
|
|
onClick={goToToday}
|
|
className="ml-4 px-3 py-1 text-sm hover:bg-primary hover:text-primary-foreground cursor-pointer"
|
|
>
|
|
Hoje
|
|
</Button>
|
|
</div>
|
|
<div className="text-sm text-muted-foreground">
|
|
{todayEvents.length} consulta{todayEvents.length !== 1 ? 's' : ''} agendada{todayEvents.length !== 1 ? 's' : ''}
|
|
</div>
|
|
</div>
|
|
|
|
{/* Lista de Pacientes do Dia */}
|
|
<div className="space-y-4">
|
|
{todayEvents.length === 0 ? (
|
|
<div className="text-center py-8 text-muted-foreground">
|
|
<CalendarIcon className="h-12 w-12 mx-auto mb-4 text-muted-foreground/50" />
|
|
<p className="text-lg mb-2">Nenhuma consulta agendada para este dia</p>
|
|
<p className="text-sm">Agenda livre para este dia</p>
|
|
</div>
|
|
) : (
|
|
todayEvents.map((appointment) => {
|
|
const paciente = pacientes.find(p => p.nome === appointment.title);
|
|
return (
|
|
<div
|
|
key={appointment.id}
|
|
className="border-l-4 p-4 rounded-lg shadow-sm bg-card border-border"
|
|
style={{ borderLeftColor: getStatusColor(appointment.type) }}
|
|
>
|
|
<div className="grid grid-cols-1 md:grid-cols-4 gap-4 items-center">
|
|
<div className="flex items-center">
|
|
<div
|
|
className="w-3 h-3 rounded-full mr-3"
|
|
style={{ backgroundColor: getStatusColor(appointment.type) }}
|
|
></div>
|
|
<div>
|
|
<div className="font-medium flex items-center">
|
|
<User className="h-4 w-4 mr-2 text-muted-foreground" />
|
|
{appointment.title}
|
|
</div>
|
|
{paciente && (
|
|
<div className="text-sm text-muted-foreground">
|
|
CPF: {paciente.cpf} • {paciente.idade} anos
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
<div className="flex items-center">
|
|
<Clock className="h-4 w-4 mr-2 text-muted-foreground" />
|
|
<span className="font-medium">{appointment.time}</span>
|
|
</div>
|
|
<div className="flex items-center">
|
|
<div
|
|
className="px-3 py-1 rounded-full text-sm font-medium text-white"
|
|
style={{ backgroundColor: getStatusColor(appointment.type) }}
|
|
>
|
|
{appointment.type}
|
|
</div>
|
|
</div>
|
|
<div className="flex items-center justify-end space-x-2">
|
|
<div className="relative group">
|
|
<Button
|
|
variant="outline"
|
|
size="sm"
|
|
className="border-primary text-primary hover:bg-primary hover:text-white cursor-pointer"
|
|
onClick={(e) => {
|
|
e.stopPropagation();
|
|
if (paciente) {
|
|
handleAbrirProntuario(paciente);
|
|
setActiveSection('prontuario');
|
|
}
|
|
}}
|
|
>
|
|
<FolderOpen className="h-4 w-4" />
|
|
</Button>
|
|
<div className="absolute bottom-full left-1/2 transform -translate-x-1/2 mb-2 px-3 py-1 bg-gray-900 dark:bg-gray-100 text-white dark:text-gray-900 text-xs rounded-md opacity-0 group-hover:opacity-100 transition-opacity duration-200 pointer-events-none whitespace-nowrap z-50">
|
|
Ver informações do paciente
|
|
<div className="absolute top-full left-1/2 transform -translate-x-1/2 w-0 h-0 border-l-4 border-r-4 border-t-4 border-transparent border-t-gray-900 dark:border-t-gray-100"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
})
|
|
)}
|
|
</div>
|
|
</section>
|
|
);
|
|
};
|
|
|
|
|
|
function PacientesSection({ handleAbrirProntuario, setActiveSection }) {
|
|
// Estados para busca de pacientes
|
|
const [buscaPaciente, setBuscaPaciente] = useState("");
|
|
const [pacientesBusca, setPacientesBusca] = useState<any[]>([]);
|
|
const [carregandoBusca, setCarregandoBusca] = useState(false);
|
|
const [erroBusca, setErroBusca] = useState<string | null>(null);
|
|
|
|
|
|
// Função para buscar pacientes
|
|
const handleBuscarPaciente = async () => {
|
|
if (!buscaPaciente.trim()) {
|
|
setPacientesBusca([]);
|
|
setErroBusca(null);
|
|
return;
|
|
}
|
|
|
|
setCarregandoBusca(true);
|
|
setErroBusca(null);
|
|
|
|
try {
|
|
// Importa a função de busca
|
|
const { buscarPacientes } = await import("@/lib/api");
|
|
const resultados = await buscarPacientes(buscaPaciente.trim());
|
|
|
|
if (resultados.length === 0) {
|
|
setErroBusca("Nenhum paciente encontrado com os critérios informados.");
|
|
setPacientesBusca([]);
|
|
} else {
|
|
// Transforma os dados da API para o formato usado no componente
|
|
const pacientesFormatados = resultados.map(p => ({
|
|
nome: p.full_name || "Nome não informado",
|
|
cpf: p.cpf || "CPF não informado",
|
|
idade: p.birth_date ? new Date().getFullYear() - new Date(p.birth_date).getFullYear() : "N/A",
|
|
statusLaudo: "Pendente", // Status padrão
|
|
id: p.id
|
|
}));
|
|
setPacientesBusca(pacientesFormatados);
|
|
setErroBusca(null);
|
|
}
|
|
} catch (error: any) {
|
|
console.error("Erro ao buscar pacientes:", error);
|
|
setErroBusca(error.message || "Erro ao buscar pacientes. Tente novamente.");
|
|
setPacientesBusca([]);
|
|
} finally {
|
|
setCarregandoBusca(false);
|
|
}
|
|
};
|
|
|
|
const handleLimparBusca = () => {
|
|
setBuscaPaciente("");
|
|
setPacientesBusca([]);
|
|
setErroBusca(null);
|
|
};
|
|
|
|
return (
|
|
<div className="bg-card shadow-md rounded-lg p-6">
|
|
<h2 className="text-2xl font-bold mb-4">Gerenciamento de Pacientes</h2>
|
|
|
|
{/* Campo de busca */}
|
|
<div className="mb-6 p-4 bg-gray-50 rounded-lg">
|
|
<h3 className="text-lg font-semibold mb-3">Buscar Paciente</h3>
|
|
<div className="flex gap-2">
|
|
<div className="flex-1">
|
|
<Input
|
|
placeholder="Digite ID, CPF, nome ou email do paciente..."
|
|
value={buscaPaciente}
|
|
onChange={(e) => setBuscaPaciente(e.target.value)}
|
|
onKeyDown={(e) => e.key === "Enter" && handleBuscarPaciente()}
|
|
className="w-full"
|
|
/>
|
|
</div>
|
|
<Button
|
|
onClick={handleBuscarPaciente}
|
|
disabled={carregandoBusca}
|
|
className="flex items-center gap-2"
|
|
>
|
|
{carregandoBusca ? (
|
|
<>
|
|
<div className="animate-spin h-4 w-4 border-2 border-white border-t-transparent rounded-full"></div>
|
|
Buscando...
|
|
</>
|
|
) : (
|
|
<>
|
|
<User className="h-4 w-4" />
|
|
Buscar
|
|
</>
|
|
)}
|
|
</Button>
|
|
{(buscaPaciente || pacientesBusca.length > 0 || erroBusca) && (
|
|
<Button
|
|
variant="outline"
|
|
onClick={handleLimparBusca}
|
|
className="flex items-center gap-2"
|
|
>
|
|
<X className="h-4 w-4" />
|
|
Limpar
|
|
</Button>
|
|
)}
|
|
</div>
|
|
|
|
{/* Resultados da busca */}
|
|
{erroBusca && (
|
|
<div className="mt-3 p-3 bg-red-50 border border-red-200 rounded-md">
|
|
<p className="text-red-700 text-sm">{erroBusca}</p>
|
|
</div>
|
|
)}
|
|
|
|
{pacientesBusca.length > 0 && (
|
|
<div className="mt-4">
|
|
<h4 className="text-md font-medium mb-2">Resultados da busca ({pacientesBusca.length}):</h4>
|
|
<div className="space-y-2">
|
|
{pacientesBusca.map((paciente, index) => (
|
|
<div key={index} className="flex items-center justify-between p-3 bg-white border rounded-lg hover:shadow-sm">
|
|
<div>
|
|
<p className="font-medium">{paciente.nome}</p>
|
|
<p className="text-sm text-gray-600">CPF: {paciente.cpf} • Idade: {paciente.idade} anos</p>
|
|
</div>
|
|
<Button
|
|
size="sm"
|
|
onClick={() => {
|
|
handleAbrirProntuario(paciente);
|
|
setActiveSection('prontuario');
|
|
}}
|
|
className="flex items-center gap-2"
|
|
>
|
|
<FolderOpen className="h-4 w-4" />
|
|
Abrir Prontuário
|
|
</Button>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
{/* Tabela de pacientes padrão */}
|
|
<div>
|
|
<h3 className="text-lg font-semibold mb-3">Pacientes Recentes</h3>
|
|
<Table>
|
|
<TableHeader>
|
|
<TableRow>
|
|
<TableHead>Paciente</TableHead>
|
|
<TableHead>CPF</TableHead>
|
|
<TableHead>Idade</TableHead>
|
|
<TableHead>Status do laudo</TableHead>
|
|
<TableHead>Ações</TableHead>
|
|
</TableRow>
|
|
</TableHeader>
|
|
<TableBody>
|
|
{pacientes.map((paciente) => (
|
|
<TableRow key={paciente.cpf}>
|
|
<TableCell className="font-medium">{paciente.nome}</TableCell>
|
|
<TableCell>{paciente.cpf}</TableCell>
|
|
<TableCell>{paciente.idade}</TableCell>
|
|
<TableCell>{paciente.statusLaudo}</TableCell>
|
|
<TableCell>
|
|
<div className="flex items-center gap-2">
|
|
<div className="relative group">
|
|
<Button
|
|
variant="outline"
|
|
size="sm"
|
|
className="border-primary text-primary hover:bg-primary hover:text-white cursor-pointer"
|
|
onClick={() => {
|
|
handleAbrirProntuario(paciente);
|
|
setActiveSection('prontuario');
|
|
}}
|
|
>
|
|
<FolderOpen className="h-4 w-4" />
|
|
</Button>
|
|
<div className="absolute bottom-full left-1/2 transform -translate-x-1/2 mb-2 px-3 py-1 bg-gray-900 dark:bg-gray-100 text-white dark:text-gray-900 text-xs rounded-md opacity-0 group-hover:opacity-100 transition-opacity duration-200 pointer-events-none whitespace-nowrap z-50">
|
|
Ver informações do paciente
|
|
<div className="absolute top-full left-1/2 transform -translate-x-1/2 w-0 h-0 border-l-4 border-r-4 border-t-4 border-transparent border-t-gray-900 dark:border-t-gray-100"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</TableCell>
|
|
</TableRow>
|
|
))}
|
|
</TableBody>
|
|
</Table>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
|
|
const renderProntuarioSection = () => (
|
|
<div className="space-y-6">
|
|
<div className="bg-card shadow-md rounded-lg p-6">
|
|
<h2 className="text-2xl font-bold mb-4">Prontuário do Paciente</h2>
|
|
|
|
{/* Informações do Paciente Selecionado */}
|
|
{pacienteSelecionado && (
|
|
<div className="bg-primary/10 border border-primary/20 rounded-lg p-4 mb-6">
|
|
<div className="flex justify-between items-center mb-2">
|
|
<h3 className="font-semibold text-primary">Dados do Paciente</h3>
|
|
<div className="flex items-center gap-2">
|
|
<Select
|
|
value={pacienteSelecionado.nome}
|
|
onValueChange={(value) => {
|
|
const paciente = pacientes.find(p => p.nome === value);
|
|
if (paciente) {
|
|
setPacienteSelecionado(paciente);
|
|
}
|
|
}}
|
|
>
|
|
<SelectTrigger className="w-48 h-8 text-xs bg-card border-primary/30 cursor-pointer">
|
|
<SelectValue />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
{pacientes.map((paciente) => (
|
|
<SelectItem key={paciente.cpf} value={paciente.nome} className="hover:bg-primary hover:text-primary-foreground">
|
|
<div className="flex items-center gap-2">
|
|
<span className="font-medium">{paciente.nome}</span>
|
|
<span className="text-xs opacity-70">({paciente.idade} anos)</span>
|
|
</div>
|
|
</SelectItem>
|
|
))}
|
|
</SelectContent>
|
|
</Select>
|
|
<Button
|
|
variant="ghost"
|
|
size="sm"
|
|
onClick={handleFecharProntuario}
|
|
className="text-primary hover:text-primary hover:bg-primary/10 h-6 w-6 p-0 cursor-pointer"
|
|
>
|
|
<X className="h-4 w-4" />
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-4 text-sm">
|
|
<div>
|
|
<span className="font-medium text-primary">Nome:</span>
|
|
<p className="text-primary/80">{pacienteSelecionado.nome}</p>
|
|
</div>
|
|
<div>
|
|
<span className="font-medium text-primary">CPF:</span>
|
|
<p className="text-primary/80">{pacienteSelecionado.cpf}</p>
|
|
</div>
|
|
<div>
|
|
<span className="font-medium text-primary">Idade:</span>
|
|
<p className="text-primary/80">{pacienteSelecionado.idade} anos</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{/* Seletor de Paciente */}
|
|
{!pacienteSelecionado && (
|
|
<div className="space-y-6">
|
|
<div className="bg-muted border rounded-lg p-6">
|
|
<div className="text-center mb-6">
|
|
<User className="h-12 w-12 mx-auto mb-4 text-muted-foreground/50" />
|
|
<h3 className="text-lg font-medium text-foreground mb-2">Selecionar Paciente</h3>
|
|
<p className="text-sm text-muted-foreground">Escolha um paciente para visualizar o prontuário completo</p>
|
|
</div>
|
|
|
|
<div className="max-w-md mx-auto">
|
|
<Label htmlFor="seletorPaciente" className="block text-sm font-medium text-foreground mb-2">
|
|
Escolha o paciente:
|
|
</Label>
|
|
<Select
|
|
onValueChange={(value) => {
|
|
const paciente = pacientes.find(p => p.nome === value);
|
|
if (paciente) {
|
|
setPacienteSelecionado(paciente);
|
|
}
|
|
}}
|
|
>
|
|
<SelectTrigger id="seletorPaciente" className="w-full cursor-pointer">
|
|
<SelectValue placeholder="Selecione um paciente..." />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
{pacientes.map((paciente) => (
|
|
<SelectItem key={paciente.cpf} value={paciente.nome} className="hover:bg-primary hover:text-primary-foreground cursor-pointer">
|
|
<div className="flex items-center gap-3 w-full">
|
|
<div className="flex-1">
|
|
<p className="font-medium">{paciente.nome}</p>
|
|
<p className="text-xs opacity-70">CPF: {paciente.cpf} • {paciente.idade} anos</p>
|
|
</div>
|
|
</div>
|
|
</SelectItem>
|
|
))}
|
|
</SelectContent>
|
|
</Select>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Cards de pacientes para seleção rápida */}
|
|
<div>
|
|
<h3 className="text-lg font-medium text-foreground mb-4">Ou selecione rapidamente:</h3>
|
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
|
{pacientes.map((paciente) => (
|
|
<div
|
|
key={paciente.cpf}
|
|
onClick={() => setPacienteSelecionado(paciente)}
|
|
className="border rounded-lg p-4 hover:shadow-md hover:border-primary transition-all cursor-pointer group"
|
|
>
|
|
<div className="flex items-center gap-3">
|
|
<div className="w-10 h-10 bg-primary/10 rounded-full flex items-center justify-center group-hover:bg-primary/20 transition-colors">
|
|
<User className="h-5 w-5 text-primary" />
|
|
</div>
|
|
<div className="flex-1 min-w-0">
|
|
<p className="font-medium text-foreground truncate">{paciente.nome}</p>
|
|
<p className="text-sm text-muted-foreground">CPF: {paciente.cpf}</p>
|
|
<p className="text-sm text-muted-foreground">{paciente.idade} anos</p>
|
|
</div>
|
|
</div>
|
|
<div className="mt-3 flex items-center justify-between">
|
|
<span className={`px-2 py-1 text-xs rounded-full ${
|
|
paciente.statusLaudo === 'Finalizado'
|
|
? 'bg-green-100 dark:bg-green-900/30 text-green-800 dark:text-green-300'
|
|
: paciente.statusLaudo === 'Pendente'
|
|
? 'bg-yellow-100 dark:bg-yellow-900/30 text-yellow-800 dark:text-yellow-300'
|
|
: 'bg-muted text-muted-foreground'
|
|
}`}>
|
|
{paciente.statusLaudo}
|
|
</span>
|
|
<Button
|
|
variant="ghost"
|
|
size="sm"
|
|
className="opacity-0 group-hover:opacity-100 transition-opacity text-primary hover:text-primary hover:bg-primary/10 cursor-pointer"
|
|
>
|
|
<FolderOpen className="h-4 w-4" />
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{/* Tabs de Navegação do Prontuário */}
|
|
{pacienteSelecionado && (
|
|
<div className="border-b border-border mb-6">
|
|
<nav className="flex space-x-8">
|
|
{[
|
|
{ id: 'nova-consulta',nome: 'Nova Consulta', icone: Plus },
|
|
{ id: 'consultas', nome: 'Consultas', icone: Stethoscope },
|
|
{ id: 'historico', nome: 'Histórico Médico', icone: History },
|
|
{ id: 'prescricoes', nome: 'Prescrições', icone: Pill },
|
|
{ id: 'exames', nome: 'Exames', icone: FileText },
|
|
{ id: 'diagnosticos', nome: 'Diagnósticos', icone: ClipboardList },
|
|
{ id: 'evolucao', nome: 'Evolução', icone: Activity },
|
|
{ id: 'anexos', nome: 'Anexos', icone: Upload }
|
|
].map((aba) => {
|
|
const Icone = aba.icone;
|
|
return (
|
|
<button
|
|
key={aba.id}
|
|
onClick={() => setAbaProntuarioAtiva(aba.id)}
|
|
className={`py-2 px-1 border-b-2 font-medium text-sm flex items-center gap-2 transition-colors cursor-pointer ${
|
|
abaProntuarioAtiva === aba.id
|
|
? 'border-primary text-primary'
|
|
: 'border-transparent text-muted-foreground hover:text-foreground hover:border-border'
|
|
}`}
|
|
>
|
|
<Icone className="h-4 w-4" />
|
|
{aba.nome}
|
|
</button>
|
|
);
|
|
})}
|
|
</nav>
|
|
</div>
|
|
)}
|
|
|
|
{/* Conteúdo das Abas */}
|
|
{pacienteSelecionado && (
|
|
<div className="min-h-[400px]">
|
|
{abaProntuarioAtiva === 'nova-consulta' && renderNovaConsultaTab()}
|
|
{abaProntuarioAtiva === 'consultas' && renderConsultasTab()}
|
|
{abaProntuarioAtiva === 'historico' && renderHistoricoTab()}
|
|
{abaProntuarioAtiva === 'prescricoes' && renderPrescricoesTab()}
|
|
{abaProntuarioAtiva === 'exames' && renderExamesTab()}
|
|
{abaProntuarioAtiva === 'diagnosticos' && renderDiagnosticosTab()}
|
|
{abaProntuarioAtiva === 'evolucao' && renderEvolucaoTab()}
|
|
{abaProntuarioAtiva === 'anexos' && renderAnexosTab()}
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
);
|
|
|
|
// Função para alterar campos da consulta atual
|
|
const handleConsultaChange = (field: string, value: string) => {
|
|
setConsultaAtual(prev => ({
|
|
...prev,
|
|
[field]: value
|
|
}));
|
|
};
|
|
|
|
// Função para salvar a consulta
|
|
const handleSalvarConsulta = () => {
|
|
if (!consultaAtual.anamnese || !consultaAtual.exameFisico) {
|
|
alert('Por favor, preencha os campos que são obrigatórios.');
|
|
return;
|
|
}
|
|
|
|
const novaConsulta = {
|
|
...consultaAtual,
|
|
id: Date.now(),
|
|
paciente: pacienteSelecionado?.nome,
|
|
dataCriacao: new Date().toLocaleString(),
|
|
profissional: medico.nome
|
|
};
|
|
|
|
setConsultasRegistradas(prev => [novaConsulta, ...prev]);
|
|
|
|
setConsultaAtual({
|
|
dataConsulta: new Date().toISOString().split('T')[0],
|
|
anamnese: "",
|
|
exameFisico: "",
|
|
hipotesesDiagnosticas: "",
|
|
condutaMedica: "",
|
|
prescricoes: "",
|
|
retornoAgendado: "",
|
|
cid10: ""
|
|
});
|
|
|
|
alert('Consulta registrada com sucesso!');
|
|
};
|
|
|
|
// Funções para renderizar cada aba do prontuário
|
|
const renderNovaConsultaTab = () => (
|
|
<div className="space-y-6">
|
|
<div className="flex justify-between items-center">
|
|
<h3 className="text-lg font-semibold">Registrar Nova Consulta</h3>
|
|
<div className="flex gap-2">
|
|
<Button className="cursor-pointer" variant="outline" onClick={() => {
|
|
setConsultaAtual({
|
|
dataConsulta: new Date().toISOString().split('T')[0],
|
|
anamnese: "",
|
|
exameFisico: "",
|
|
hipotesesDiagnosticas: "",
|
|
condutaMedica: "",
|
|
prescricoes: "",
|
|
retornoAgendado: "",
|
|
cid10: ""
|
|
});
|
|
}}>
|
|
Limpar Formulário
|
|
</Button>
|
|
<Button onClick={handleSalvarConsulta} className="flex items-center gap-2 cursor-pointer">
|
|
<Plus className="h-4 w-4" />
|
|
Salvar Consulta
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="bg-card border rounded-lg p-6 space-y-6">
|
|
{/* Data da Consulta */}
|
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
|
<div className="space-y-2">
|
|
<Label htmlFor="dataConsulta" className="text-sm font-medium text-foreground">
|
|
Data da Consulta *
|
|
</Label>
|
|
<Input
|
|
id="dataConsulta"
|
|
type="date"
|
|
value={consultaAtual.dataConsulta}
|
|
onChange={(e) => handleConsultaChange('dataConsulta', e.target.value)}
|
|
className="w-full"
|
|
/>
|
|
</div>
|
|
|
|
<div className="space-y-2">
|
|
<Label htmlFor="cid10" className="text-sm font-medium text-foreground">
|
|
CID-10
|
|
</Label>
|
|
<Input
|
|
id="cid10"
|
|
value={consultaAtual.cid10}
|
|
onChange={(e) => handleConsultaChange('cid10', e.target.value)}
|
|
placeholder="Ex: I10, E11, etc."
|
|
className="w-full"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Anamnese */}
|
|
<div className="space-y-2">
|
|
<Label htmlFor="anamnese" className="text-sm font-medium text-foreground">
|
|
Anamnese *
|
|
</Label>
|
|
<Textarea
|
|
id="anamnese"
|
|
value={consultaAtual.anamnese}
|
|
onChange={(e) => handleConsultaChange('anamnese', e.target.value)}
|
|
placeholder="Descreva a história clínica do paciente, queixas principais, histórico da doença atual..."
|
|
rows={4}
|
|
className="w-full"
|
|
/>
|
|
</div>
|
|
|
|
{/* Exame Físico */}
|
|
<div className="space-y-2">
|
|
<Label htmlFor="exameFisico" className="text-sm font-medium text-foreground">
|
|
Exame Físico *
|
|
</Label>
|
|
<Textarea
|
|
id="exameFisico"
|
|
value={consultaAtual.exameFisico}
|
|
onChange={(e) => handleConsultaChange('exameFisico', e.target.value)}
|
|
placeholder="Descreva os achados do exame físico: sinais vitais, inspeção, palpação, ausculta, percussão..."
|
|
rows={4}
|
|
className="w-full"
|
|
/>
|
|
</div>
|
|
|
|
{/* Hipóteses Diagnósticas */}
|
|
<div className="space-y-2">
|
|
<Label htmlFor="hipotesesDiagnosticas" className="text-sm font-medium text-foreground">
|
|
Hipóteses Diagnósticas
|
|
</Label>
|
|
<Textarea
|
|
id="hipotesesDiagnosticas"
|
|
value={consultaAtual.hipotesesDiagnosticas}
|
|
onChange={(e) => handleConsultaChange('hipotesesDiagnosticas', e.target.value)}
|
|
placeholder="Liste as principais hipóteses diagnósticas em ordem de probabilidade..."
|
|
rows={3}
|
|
className="w-full"
|
|
/>
|
|
</div>
|
|
|
|
{/* Conduta Médica */}
|
|
<div className="space-y-2">
|
|
<Label htmlFor="condutaMedica" className="text-sm font-medium text-foreground">
|
|
Conduta Médica
|
|
</Label>
|
|
<Textarea
|
|
id="condutaMedica"
|
|
value={consultaAtual.condutaMedica}
|
|
onChange={(e) => handleConsultaChange('condutaMedica', e.target.value)}
|
|
placeholder="Descreva a conduta médica adotada, orientações gerais, solicitação de exames complementares..."
|
|
rows={3}
|
|
className="w-full"
|
|
/>
|
|
</div>
|
|
|
|
{/* Prescrições */}
|
|
<div className="space-y-2">
|
|
<Label htmlFor="prescricoes" className="text-sm font-medium text-foreground">
|
|
Prescrições
|
|
</Label>
|
|
<Textarea
|
|
id="prescricoes"
|
|
value={consultaAtual.prescricoes}
|
|
onChange={(e) => handleConsultaChange('prescricoes', e.target.value)}
|
|
placeholder="Liste as prescrições: medicamentos, dosagens, frequência, duração do tratamento..."
|
|
rows={4}
|
|
className="w-full"
|
|
/>
|
|
</div>
|
|
|
|
{/* Retorno Agendado */}
|
|
<div className="space-y-2">
|
|
<Label htmlFor="retornoAgendado" className="text-sm font-medium text-foreground">
|
|
Retorno Agendado
|
|
</Label>
|
|
<Input
|
|
id="retornoAgendado"
|
|
type="date"
|
|
value={consultaAtual.retornoAgendado}
|
|
onChange={(e) => handleConsultaChange('retornoAgendado', e.target.value)}
|
|
className="w-full"
|
|
/>
|
|
</div>
|
|
|
|
{/* Informações do Registro */}
|
|
<div className="border-t pt-4">
|
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-4 text-sm text-muted-foreground">
|
|
<div>
|
|
<span className="font-medium">Paciente:</span>
|
|
<p>{pacienteSelecionado?.nome}</p>
|
|
</div>
|
|
<div>
|
|
<span className="font-medium">Profissional:</span>
|
|
<p>{medico.nome}</p>
|
|
</div>
|
|
<div>
|
|
<span className="font-medium">Data do Registro:</span>
|
|
<p>{new Date().toLocaleDateString('pt-BR')}</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Consultas Anteriores do Paciente */}
|
|
{consultasRegistradas.length > 0 && (
|
|
<div className="bg-card border rounded-lg p-6">
|
|
<h4 className="text-lg font-medium mb-4">Consultas Anteriores</h4>
|
|
<div className="space-y-3">
|
|
{consultasRegistradas
|
|
.filter(consulta => consulta.paciente === pacienteSelecionado?.nome)
|
|
.slice(0, 3)
|
|
.map((consulta) => (
|
|
<div key={consulta.id} className="border rounded-lg p-3 hover:shadow-sm">
|
|
<div className="flex justify-between items-start mb-2">
|
|
<div>
|
|
<p className="font-medium text-sm">
|
|
Consulta de {new Date(consulta.dataConsulta).toLocaleDateString('pt-BR')}
|
|
</p>
|
|
<p className="text-xs text-muted-foreground">
|
|
Registrada em: {consulta.dataCriacao}
|
|
</p>
|
|
</div>
|
|
{consulta.cid10 && (
|
|
<span className="px-2 py-1 bg-blue-100 dark:bg-blue-900/30 text-blue-800 dark:text-blue-300 text-xs rounded-full">
|
|
{consulta.cid10}
|
|
</span>
|
|
)}
|
|
</div>
|
|
<div className="text-sm text-foreground">
|
|
<p><strong>Anamnese:</strong> {consulta.anamnese.substring(0, 100)}...</p>
|
|
{consulta.hipotesesDiagnosticas && (
|
|
<p><strong>Diagnóstico:</strong> {consulta.hipotesesDiagnosticas.substring(0, 80)}...</p>
|
|
)}
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
|
|
const renderConsultasTab = () => (
|
|
<div className="space-y-6">
|
|
<div className="flex justify-between items-center">
|
|
<h3 className="text-lg font-semibold">Registro de Consultas</h3>
|
|
</div>
|
|
|
|
<div className="space-y-4">
|
|
<div className="border rounded-lg p-4">
|
|
<div className="flex justify-between items-start mb-3">
|
|
<div>
|
|
<h4 className="font-medium">Consulta Cardiológica</h4>
|
|
<p className="text-sm text-muted-foreground">27/09/2025 - 09:00</p>
|
|
</div>
|
|
<span className="px-2 py-1 bg-green-100 dark:bg-green-900/30 text-green-800 dark:text-green-300 text-xs rounded-full">Finalizada</span>
|
|
</div>
|
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 text-sm">
|
|
<div>
|
|
<span className="font-medium">Motivo:</span>
|
|
<p>Dor no peito e falta de ar</p>
|
|
</div>
|
|
<div>
|
|
<span className="font-medium">Duração:</span>
|
|
<p>45 minutos</p>
|
|
</div>
|
|
</div>
|
|
<div className="mt-3">
|
|
<span className="font-medium">Observações:</span>
|
|
<p className="text-sm mt-1">Paciente relatou melhora dos sintomas após início do tratamento. Pressão arterial controlada.</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="border rounded-lg p-4">
|
|
<div className="flex justify-between items-start mb-3">
|
|
<div>
|
|
<h4 className="font-medium">Consulta Dermatológica</h4>
|
|
<p className="text-sm text-muted-foreground">15/09/2025 - 14:30</p>
|
|
</div>
|
|
<span className="px-2 py-1 bg-blue-100 dark:bg-blue-900/30 text-blue-800 dark:text-blue-300 text-xs rounded-full">Retorno Agendado</span>
|
|
</div>
|
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 text-sm">
|
|
<div>
|
|
<span className="font-medium">Motivo:</span>
|
|
<p>Avaliação de lesão cutânea</p>
|
|
</div>
|
|
<div>
|
|
<span className="font-medium">Duração:</span>
|
|
<p>30 minutos</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
|
|
const renderHistoricoTab = () => (
|
|
<div className="space-y-6">
|
|
<div className="flex justify-between items-center">
|
|
<h3 className="text-lg font-semibold">Histórico Médico Completo</h3>
|
|
<Button className="flex items-center gap-2 cursor-pointer">
|
|
<Plus className="h-4 w-4" />
|
|
Adicionar Registro
|
|
</Button>
|
|
</div>
|
|
|
|
<div className="space-y-4">
|
|
<div className="border rounded-lg p-4">
|
|
<h4 className="font-medium mb-2">Condições Pré-existentes</h4>
|
|
<ul className="list-disc list-inside text-sm space-y-1">
|
|
<li>Hipertensão arterial (diagnosticada em 2020)</li>
|
|
<li>Diabetes tipo 2 (diagnosticada em 2018)</li>
|
|
<li>Histórico familiar de doenças cardiovasculares</li>
|
|
</ul>
|
|
</div>
|
|
|
|
<div className="border rounded-lg p-4">
|
|
<h4 className="font-medium mb-2">Cirurgias Anteriores</h4>
|
|
<div className="space-y-2 text-sm">
|
|
<div className="flex justify-between">
|
|
<span>Apendicectomia</span>
|
|
<span className="text-muted-foreground">15/03/2010</span>
|
|
</div>
|
|
<div className="flex justify-between">
|
|
<span>Colecistectomia laparoscópica</span>
|
|
<span className="text-muted-foreground">22/08/2019</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="border rounded-lg p-4">
|
|
<h4 className="font-medium mb-2">Alergias e Reações Adversas</h4>
|
|
<div className="space-y-2 text-sm">
|
|
<div className="flex items-center gap-2">
|
|
<span className="px-2 py-1 bg-red-100 dark:bg-red-900/30 text-red-800 dark:text-red-300 text-xs rounded">Alergia</span>
|
|
<span>Penicilina - reação cutânea</span>
|
|
</div>
|
|
<div className="flex items-center gap-2">
|
|
<span className="px-2 py-1 bg-yellow-100 dark:bg-yellow-900/30 text-yellow-800 dark:text-yellow-300 text-xs rounded">Intolerância</span>
|
|
<span>Lactose - sintomas gastrintestinais</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
|
|
const renderPrescricoesTab = () => (
|
|
<div className="space-y-6">
|
|
<div className="flex justify-between items-center">
|
|
<h3 className="text-lg font-semibold text-foreground">Prescrições Médicas</h3>
|
|
<Button className="flex items-center gap-2 cursor-pointer">
|
|
<Plus className="h-4 w-4" />
|
|
Nova Prescrição
|
|
</Button>
|
|
</div>
|
|
|
|
<div className="space-y-4">
|
|
<div className="border rounded-lg p-4">
|
|
<div className="flex justify-between items-start mb-3">
|
|
<div>
|
|
<h4 className="font-medium text-foreground">Prescrição Atual</h4>
|
|
<p className="text-sm text-muted-foreground">Prescrita em 27/09/2025</p>
|
|
</div>
|
|
<span className="px-2 py-1 bg-green-100 dark:bg-green-900/30 text-green-800 dark:text-green-300 text-xs rounded-full">Ativa</span>
|
|
</div>
|
|
<div className="space-y-3">
|
|
<div className="border-l-4 border-blue-500 pl-4">
|
|
<div className="flex justify-between items-start">
|
|
<div>
|
|
<p className="font-medium text-foreground">Losartana 50mg</p>
|
|
<p className="text-sm text-muted-foreground">1 comprimido pela manhã</p>
|
|
<p className="text-sm text-muted-foreground">Duração: 30 dias</p>
|
|
</div>
|
|
<Button variant="outline" size="sm" className="cursor-pointer">
|
|
<Eye className="h-4 w-4" />
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
<div className="border-l-4 border-green-500 pl-4">
|
|
<div className="flex justify-between items-start">
|
|
<div>
|
|
<p className="font-medium text-foreground">Metformina 850mg</p>
|
|
<p className="text-sm text-muted-foreground">1 comprimido após café e jantar</p>
|
|
<p className="text-sm text-muted-foreground">Duração: 60 dias</p>
|
|
</div>
|
|
<Button variant="outline" size="sm" className="cursor-pointer">
|
|
<Eye className="h-4 w-4" />
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="border rounded-lg p-4">
|
|
<div className="flex justify-between items-start mb-3">
|
|
<div>
|
|
<h4 className="font-medium text-foreground">Prescrições Anteriores</h4>
|
|
<p className="text-sm text-muted-foreground">Histórico de medicamentos</p>
|
|
</div>
|
|
</div>
|
|
<div className="space-y-2 text-sm">
|
|
<div className="flex justify-between items-center py-2 border-b border-border">
|
|
<div>
|
|
<p className="font-medium text-foreground">Sinvastatina 20mg</p>
|
|
<p className="text-muted-foreground">Prescrita em 15/08/2025 - Finalizada</p>
|
|
</div>
|
|
<Button variant="ghost" size="sm" className="cursor-pointer">
|
|
<History className="h-4 w-4" />
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
|
|
const renderExamesTab = () => (
|
|
<div className="space-y-6">
|
|
<div className="flex justify-between items-center">
|
|
<h3 className="text-lg font-semibold text-foreground">Exames Solicitados</h3>
|
|
<Button className="flex items-center gap-2 cursor-pointer">
|
|
<Plus className="h-4 w-4" />
|
|
Solicitar Exame
|
|
</Button>
|
|
</div>
|
|
|
|
<div className="space-y-4">
|
|
<div className="border rounded-lg p-4">
|
|
<div className="flex justify-between items-start mb-3">
|
|
<div>
|
|
<h4 className="font-medium text-foreground">Exames Pendentes</h4>
|
|
</div>
|
|
</div>
|
|
<div className="space-y-3">
|
|
<div className="flex justify-between items-center p-3 bg-yellow-50 dark:bg-yellow-900/20 border border-yellow-200 dark:border-yellow-700 rounded">
|
|
<div>
|
|
<p className="font-medium text-foreground">Ecocardiograma</p>
|
|
<p className="text-sm text-muted-foreground">Solicitado em 25/09/2025</p>
|
|
<p className="text-sm text-muted-foreground">Urgência: Normal</p>
|
|
</div>
|
|
<span className="px-2 py-1 bg-yellow-100 dark:bg-yellow-800 text-yellow-800 dark:text-yellow-100 text-xs rounded-full">Pendente</span>
|
|
</div>
|
|
|
|
<div className="flex justify-between items-center p-3 bg-blue-50 dark:bg-blue-900/20 border border-blue-200 dark:border-blue-700 rounded">
|
|
<div>
|
|
<p className="font-medium text-foreground">Hemograma Completo</p>
|
|
<p className="text-sm text-muted-foreground">Solicitado em 27/09/2025</p>
|
|
<p className="text-sm text-muted-foreground">Urgência: Normal</p>
|
|
</div>
|
|
<span className="px-2 py-1 bg-blue-100 dark:bg-blue-800 text-blue-800 dark:text-blue-100 text-xs rounded-full">Agendado</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="border rounded-lg p-4">
|
|
<div className="flex justify-between items-start mb-3">
|
|
<div>
|
|
<h4 className="font-medium text-foreground">Resultados Disponíveis</h4>
|
|
</div>
|
|
</div>
|
|
<div className="space-y-3">
|
|
<div className="flex justify-between items-center p-3 bg-green-50 dark:bg-green-900/20 border border-green-200 dark:border-green-700 rounded">
|
|
<div>
|
|
<p className="font-medium text-foreground">Glicemia de Jejum</p>
|
|
<p className="text-sm text-muted-foreground">Realizado em 20/09/2025</p>
|
|
<p className="text-sm font-medium text-green-700 dark:text-green-400">Resultado: 95 mg/dL (Normal)</p>
|
|
</div>
|
|
<div className="flex gap-2">
|
|
<Button variant="outline" size="sm" className="cursor-pointer">
|
|
<Eye className="h-4 w-4" />
|
|
</Button>
|
|
<Button variant="outline" size="sm" className="cursor-pointer">
|
|
<Download className="h-4 w-4" />
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
|
|
const renderDiagnosticosTab = () => (
|
|
<div className="space-y-6">
|
|
<div className="flex justify-between items-center">
|
|
<h3 className="text-lg font-semibold text-foreground">Diagnósticos</h3>
|
|
<Button className="flex items-center gap-2 cursor-pointer">
|
|
<Plus className="h-4 w-4" />
|
|
Novo Diagnóstico
|
|
</Button>
|
|
</div>
|
|
|
|
<div className="space-y-4">
|
|
<div className="border rounded-lg p-4">
|
|
<h4 className="font-medium mb-3 text-foreground">Diagnósticos Ativos</h4>
|
|
<div className="space-y-3">
|
|
<div className="border-l-4 border-red-500 pl-4">
|
|
<div className="flex justify-between items-start">
|
|
<div>
|
|
<p className="font-medium text-foreground">Hipertensão Arterial Sistêmica</p>
|
|
<p className="text-sm text-muted-foreground">CID-10: I10</p>
|
|
<p className="text-sm text-muted-foreground">Diagnosticado em: 15/03/2020</p>
|
|
<p className="text-sm mt-1 text-foreground">Status: Controlada com medicação</p>
|
|
</div>
|
|
<span className="px-2 py-1 bg-red-100 dark:bg-red-900/30 text-red-800 dark:text-red-300 text-xs rounded-full">Ativo</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="border-l-4 border-orange-500 pl-4">
|
|
<div className="flex justify-between items-start">
|
|
<div>
|
|
<p className="font-medium text-foreground">Diabetes Mellitus Tipo 2</p>
|
|
<p className="text-sm text-muted-foreground">CID-10: E11</p>
|
|
<p className="text-sm text-muted-foreground">Diagnosticado em: 10/08/2018</p>
|
|
<p className="text-sm mt-1 text-foreground">Status: Controlada com dieta e medicação</p>
|
|
</div>
|
|
<span className="px-2 py-1 bg-orange-100 dark:bg-orange-900/30 text-orange-800 dark:text-orange-300 text-xs rounded-full">Ativo</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="border rounded-lg p-4">
|
|
<h4 className="font-medium mb-3 text-foreground">Histórico de Diagnósticos</h4>
|
|
<div className="space-y-2 text-sm">
|
|
<div className="flex justify-between items-center py-2 border-b">
|
|
<div>
|
|
<p className="font-medium text-foreground">Gastrite Aguda</p>
|
|
<p className="text-muted-foreground">CID-10: K29.0 - Resolvido em 2023</p>
|
|
</div>
|
|
<span className="px-2 py-1 bg-muted text-muted-foreground text-xs rounded-full">Resolvido</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
|
|
const renderEvolucaoTab = () => (
|
|
<div className="space-y-6">
|
|
<div className="flex justify-between items-center">
|
|
<h3 className="text-lg font-semibold text-foreground">Evolução do Quadro</h3>
|
|
<Button className="flex items-center gap-2 cursor-pointer">
|
|
<Plus className="h-4 w-4" />
|
|
Nova Evolução
|
|
</Button>
|
|
</div>
|
|
|
|
<div className="space-y-4">
|
|
<div className="border rounded-lg p-4">
|
|
<div className="flex justify-between items-start mb-3">
|
|
<div>
|
|
<h4 className="font-medium text-foreground">Evolução Recente</h4>
|
|
<p className="text-sm text-muted-foreground">27/09/2025 - 09:15</p>
|
|
</div>
|
|
<span className="px-2 py-1 bg-blue-100 dark:bg-blue-900/30 text-blue-800 dark:text-blue-300 text-xs rounded-full">Melhora</span>
|
|
</div>
|
|
<div className="space-y-2">
|
|
<p className="text-sm text-foreground"><strong>Subjetivo:</strong> Paciente relatou diminuição significativa da dor no peito e melhora da capacidade respiratória.</p>
|
|
<p className="text-sm text-foreground"><strong>Objetivo:</strong> PA: 130/80 mmHg, FC: 72 bpm, ausculta cardíaca sem alterações.</p>
|
|
<p className="text-sm text-foreground"><strong>Avaliação:</strong> Resposta positiva ao tratamento iniciado, pressão arterial em níveis aceitáveis.</p>
|
|
<p className="text-sm text-foreground"><strong>Plano:</strong> Manter medicação atual, retorno em 30 dias.</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="border rounded-lg p-4">
|
|
<div className="flex justify-between items-start mb-3">
|
|
<div>
|
|
<h4 className="font-medium text-foreground">Evolução Anterior</h4>
|
|
<p className="text-sm text-muted-foreground">15/09/2025 - 14:45</p>
|
|
</div>
|
|
<span className="px-2 py-1 bg-yellow-100 dark:bg-yellow-900/30 text-yellow-800 dark:text-yellow-300 text-xs rounded-full">Estável</span>
|
|
</div>
|
|
<div className="space-y-2">
|
|
<p className="text-sm"><strong>Subjetivo:</strong> Paciente apresentou episódios esporádicos de dor torácica leve.</p>
|
|
<p className="text-sm"><strong>Objetivo:</strong> Exame físico sem alterações significativas.</p>
|
|
<p className="text-sm"><strong>Plano:</strong> Ajuste da medicação e solicitação de exames complementares.</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
|
|
const renderAnexosTab = () => (
|
|
<div className="space-y-6">
|
|
<div className="flex justify-between items-center">
|
|
<h3 className="text-lg font-semibold text-foreground">Anexos (Exames, Imagens)</h3>
|
|
<Button className="flex items-center gap-2 cursor-pointer">
|
|
<Upload className="h-4 w-4" />
|
|
Adicionar Anexo
|
|
</Button>
|
|
</div>
|
|
|
|
<div className="space-y-4">
|
|
<div className="border rounded-lg p-4">
|
|
<h4 className="font-medium mb-3">Exames de Imagem</h4>
|
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
<div className="border rounded-lg p-3 hover:shadow-md transition-shadow">
|
|
<div className="flex items-center gap-3 mb-2">
|
|
<div className="w-10 h-10 bg-blue-100 rounded-lg flex items-center justify-center">
|
|
<FileText className="h-5 w-5 text-blue-600" />
|
|
</div>
|
|
<div>
|
|
<p className="font-medium text-sm">Radiografia de Tórax</p>
|
|
<p className="text-xs text-muted-foreground">20/09/2025</p>
|
|
</div>
|
|
</div>
|
|
<div className="flex gap-2">
|
|
<Button variant="outline" size="sm" className="flex-1 cursor-pointer">
|
|
<Eye className="h-3 w-3 mr-1" />
|
|
Visualizar
|
|
</Button>
|
|
<Button variant="outline" size="sm" className="flex-1 cursor-pointer">
|
|
<Download className="h-3 w-3 mr-1" />
|
|
Download
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="border rounded-lg p-3 hover:shadow-md transition-shadow">
|
|
<div className="flex items-center gap-3 mb-2">
|
|
<div className="w-10 h-10 bg-green-100 rounded-lg flex items-center justify-center">
|
|
<FileText className="h-5 w-5 text-green-600" />
|
|
</div>
|
|
<div>
|
|
<p className="font-medium text-sm">ECG</p>
|
|
<p className="text-xs text-muted-foreground">15/09/2025</p>
|
|
</div>
|
|
</div>
|
|
<div className="flex gap-2">
|
|
<Button variant="outline" size="sm" className="flex-1 cursor-pointer">
|
|
<Eye className="h-3 w-3 mr-1" />
|
|
Visualizar
|
|
</Button>
|
|
<Button variant="outline" size="sm" className="flex-1 cursor-pointer">
|
|
<Download className="h-3 w-3 mr-1" />
|
|
Download
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="border rounded-lg p-4">
|
|
<h4 className="font-medium mb-3">Laudos e Documentos</h4>
|
|
<div className="space-y-3">
|
|
<div className="flex items-center justify-between p-3 border rounded-lg hover:bg-muted/50">
|
|
<div className="flex items-center gap-3">
|
|
<div className="w-8 h-8 bg-purple-100 rounded flex items-center justify-center">
|
|
<FileText className="h-4 w-4 text-purple-600" />
|
|
</div>
|
|
<div>
|
|
<p className="font-medium text-sm">Laudo de Ecocardiograma</p>
|
|
<p className="text-xs text-muted-foreground">10/08/2025 - Dr. Carlos Andrade</p>
|
|
</div>
|
|
</div>
|
|
<div className="flex gap-2">
|
|
<Button variant="outline" size="sm" className="cursor-pointer">
|
|
<Eye className="h-3 w-3" />
|
|
</Button>
|
|
<Button variant="outline" size="sm" className="cursor-pointer">
|
|
<Download className="h-3 w-3" />
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="flex items-center justify-between p-3 border rounded-lg hover:bg-muted/50">
|
|
<div className="flex items-center gap-3">
|
|
<div className="w-8 h-8 bg-orange-100 rounded flex items-center justify-center">
|
|
<FileText className="h-4 w-4 text-orange-600" />
|
|
</div>
|
|
<div>
|
|
<p className="font-medium text-sm">Relatório de Consulta Especializada</p>
|
|
<p className="text-xs text-gray-600">05/09/2025 - Cardiologia</p>
|
|
</div>
|
|
</div>
|
|
<div className="flex gap-2">
|
|
<Button variant="outline" size="sm" className="cursor-pointer">
|
|
<Eye className="h-3 w-3" />
|
|
</Button>
|
|
<Button variant="outline" size="sm" className="cursor-pointer">
|
|
<Download className="h-3 w-3" />
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
|
|
|
|
const renderLaudosSection = () => (
|
|
<section>
|
|
<LaudoEditor />
|
|
</section>
|
|
);
|
|
// --- 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<any>(null);
|
|
const [cid, setCid] = useState("");
|
|
const [imagem, setImagem] = useState<string | null>(null);
|
|
const [assinatura, setAssinatura] = useState<string | null>(null);
|
|
const sigCanvasRef = useRef<any>(null);
|
|
const [laudos, setLaudos] = useState<any[]>([]);
|
|
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 (
|
|
<div className="min-h-screen bg-gradient-to-br from-primary/5 to-white p-2 md:p-4">
|
|
<div className="max-w-2xl mx-auto">
|
|
<div className="bg-white border border-primary/10 shadow-lg rounded-xl p-4 md:p-6 mb-6">
|
|
<h2 className="text-2xl font-bold text-primary mb-4 text-center tracking-tight">Laudo Médico</h2>
|
|
|
|
{!pacienteSelecionado ? (
|
|
<div className="flex flex-col items-center justify-center py-8 px-1 bg-white rounded-xl shadow-sm">
|
|
<div className="mb-2">
|
|
<svg width="40" height="40" fill="none" viewBox="0 0 24 24" className="mx-auto text-gray-400">
|
|
<circle cx="12" cy="8" r="4" stroke="currentColor" strokeWidth="2"/>
|
|
<path d="M4 20c0-2.5 3.5-4.5 8-4.5s8 2 8 4.5" stroke="currentColor" strokeWidth="2"/>
|
|
</svg>
|
|
</div>
|
|
<h2 className="text-lg font-bold text-center text-gray-800 mb-1">Selecionar Paciente</h2>
|
|
<p className="text-gray-500 text-center mb-4 max-w-xs text-sm">Escolha um paciente para visualizar o prontuário completo</p>
|
|
<div className="w-full max-w-xs">
|
|
<label htmlFor="select-paciente" className="block text-sm font-medium text-gray-700 mb-1">Escolha o paciente:</label>
|
|
<div className="relative">
|
|
<select
|
|
id="select-paciente"
|
|
className="w-full p-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-200 focus:border-blue-300 focus:outline-none bg-white text-sm text-gray-900 shadow-sm transition-all appearance-none"
|
|
onChange={e => {
|
|
const p = pacientes.find(p => p.cpf === e.target.value);
|
|
if (p) handleSelectPaciente(p);
|
|
}}
|
|
defaultValue=""
|
|
>
|
|
<option value="" className="text-gray-400">Selecione um paciente...</option>
|
|
{pacientes.map(p => (
|
|
<option key={p.cpf} value={p.cpf}>{p.nome} - {p.cpf}</option>
|
|
))}
|
|
</select>
|
|
<span className="absolute right-2 top-1/2 -translate-y-1/2 text-gray-400 pointer-events-none">
|
|
<svg width="16" height="16" fill="none" viewBox="0 0 24 24"><path d="M12 14l-4-4h8l-4 4z" fill="currentColor"/></svg>
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
) : (
|
|
<div className="bg-primary/10 border border-primary/20 rounded-lg p-3 mb-4 flex flex-col md:flex-row md:items-center md:justify-between gap-2">
|
|
<div>
|
|
<div className="font-bold text-primary text-base mb-0.5">{pacienteSelecionado.nome}</div>
|
|
<div className="text-xs text-gray-700">CPF: {pacienteSelecionado.cpf}</div>
|
|
<div className="text-xs text-gray-700">Idade: {pacienteSelecionado.idade}</div>
|
|
<div className="text-xs text-gray-700">Sexo: {pacienteSelecionado.sexo}</div>
|
|
</div>
|
|
<button type="button" onClick={limparPaciente} className="px-3 py-1 rounded bg-red-100 text-red-700 hover:bg-red-200 font-semibold shadow text-xs">Trocar paciente</button>
|
|
</div>
|
|
)}
|
|
|
|
<form className="space-y-4">
|
|
<div className="space-y-1">
|
|
<label className="block text-sm font-semibold text-primary">CID</label>
|
|
<input
|
|
type="text"
|
|
value={cid}
|
|
onChange={e => 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"
|
|
/>
|
|
</div>
|
|
<div className="space-y-1">
|
|
<label className="block text-sm font-semibold text-primary">Conteúdo do Laudo *</label>
|
|
{!preview ? (
|
|
<ReactQuill
|
|
value={conteudo}
|
|
onChange={setConteudo}
|
|
modules={{
|
|
toolbar: [
|
|
['bold', 'italic', 'underline'],
|
|
[{ 'list': 'ordered' }, { 'list': 'bullet' }],
|
|
[{ 'align': [] }],
|
|
[{ 'size': ['small', false, 'large', 'huge'] }],
|
|
['clean']
|
|
]
|
|
}}
|
|
className="h-40 border border-primary/20 rounded-md text-sm"
|
|
/>
|
|
) : (
|
|
<div className="border border-primary/20 p-2 rounded-md bg-muted overflow-auto">
|
|
<h3 className="text-base font-semibold mb-1 text-primary">Pré-visualização:</h3>
|
|
<div dangerouslySetInnerHTML={{ __html: conteudo }} />
|
|
</div>
|
|
)}
|
|
</div>
|
|
<div className="space-y-1">
|
|
<label className="block text-sm font-semibold text-primary mb-2">Imagem (opcional)</label>
|
|
<div className="mb-2"></div>
|
|
<input
|
|
type="file"
|
|
accept="image/*"
|
|
onChange={e => {
|
|
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 && (
|
|
<img src={imagem} alt="Pré-visualização" className="mt-1 rounded-md max-h-20 border border-primary/20" />
|
|
)}
|
|
</div>
|
|
<div className="space-y-1">
|
|
<label className="block text-sm font-semibold text-primary">Assinatura Digital</label>
|
|
<div className="bg-muted rounded-md border border-primary/20 p-2 flex flex-col items-center">
|
|
<SignatureCanvas
|
|
ref={sigCanvasRef}
|
|
penColor="#0f172a"
|
|
backgroundColor="#fff"
|
|
canvasProps={{ width: 220, height: 60, className: "rounded-md border bg-white shadow" }}
|
|
onEnd={() => setAssinatura(sigCanvasRef.current?.isEmpty() ? null : sigCanvasRef.current?.toDataURL())}
|
|
/>
|
|
<div className="flex gap-2 mt-2">
|
|
<button type="button" onClick={() => { sigCanvasRef.current?.clear(); setAssinatura(null); }} className="px-2 py-1 text-xs rounded-md bg-muted-foreground text-white hover:bg-muted font-semibold shadow">Limpar</button>
|
|
</div>
|
|
{assinatura && (
|
|
<img src={assinatura} alt="Assinatura" className="mt-2 max-h-10 border rounded-md bg-white shadow" />
|
|
)}
|
|
</div>
|
|
</div>
|
|
<div className="flex flex-col md:flex-row gap-2 mt-4">
|
|
<button
|
|
type="button"
|
|
onClick={() => salvarLaudo("Rascunho")}
|
|
className="w-full md:w-1/3 flex items-center justify-center gap-1 bg-gray-100 text-gray-700 py-2 rounded-lg font-semibold text-base shadow-sm hover:bg-gray-200 transition-all border border-gray-200"
|
|
>
|
|
<svg className="w-4 h-4 text-gray-400" fill="none" stroke="currentColor" strokeWidth="2" viewBox="0 0 24 24"><path d="M5 13l4 4L19 7" strokeLinecap="round" strokeLinejoin="round"/></svg>
|
|
Salvar Rascunho
|
|
</button>
|
|
<button
|
|
type="button"
|
|
onClick={() => salvarLaudo("Entregue")}
|
|
className="w-full md:w-1/3 flex items-center justify-center gap-1 bg-primary text-white py-2 rounded-lg font-semibold text-base shadow-sm hover:bg-primary/90 transition-all border border-primary/20"
|
|
>
|
|
<svg className="w-4 h-4 text-white" fill="none" stroke="currentColor" strokeWidth="2" viewBox="0 0 24 24"><path d="M9 12l2 2l4-4" strokeLinecap="round" strokeLinejoin="round"/></svg>
|
|
Liberar Laudo
|
|
</button>
|
|
<button
|
|
type="button"
|
|
onClick={() => setPreview(!preview)}
|
|
className="w-full md:w-1/3 flex items-center justify-center gap-1 bg-white text-primary py-2 rounded-lg font-semibold text-base shadow-sm hover:bg-primary/10 transition-all border border-primary/20"
|
|
>
|
|
<svg className="w-4 h-4 text-primary" fill="none" stroke="currentColor" strokeWidth="2" viewBox="0 0 24 24"><path d="M15 10l4.553 2.276A2 2 0 0121 14.09V17a2 2 0 01-2 2H5a2 2 0 01-2-2v-2.91a2 2 0 01.447-1.814L8 10m7-4v4m0 0l-4 4m4-4l4 4" strokeLinecap="round" strokeLinejoin="round"/></svg>
|
|
Pré-visualizar Laudo
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
|
|
<div className="bg-white border border-primary/10 shadow-lg rounded-xl p-4 md:p-6">
|
|
<h3 className="text-xl font-bold text-primary mb-3 text-center">Histórico de Laudos</h3>
|
|
{laudos.length === 0 ? (
|
|
<p className="text-muted-foreground text-center text-sm">Nenhum laudo registrado.</p>
|
|
) : (
|
|
laudos.map((laudo: any, idx: number) => (
|
|
<div key={idx} className="border border-primary/20 rounded-2xl p-4 mb-6 bg-primary/5 shadow-sm">
|
|
<div className="flex flex-col md:flex-row md:justify-between md:items-center mb-3 gap-2">
|
|
<div className="flex items-center gap-2">
|
|
<svg className="w-7 h-7 text-primary" fill="none" viewBox="0 0 24 24"><circle cx="12" cy="8" r="4" stroke="currentColor" strokeWidth="2"/><path d="M4 20c0-2.5 3.5-4.5 8-4.5s8 2 8 4.5" stroke="currentColor" strokeWidth="2"/></svg>
|
|
<span className="font-bold text-lg md:text-xl text-primary drop-shadow-sm">{laudo.paciente}</span>
|
|
</div>
|
|
<span className="text-base text-gray-500 font-medium">CPF: {laudo.cpf}</span>
|
|
</div>
|
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-2 mb-2 text-sm text-gray-700">
|
|
<div>Idade: <span className="font-medium">{laudo.idade}</span></div>
|
|
<div>Sexo: <span className="font-medium">{laudo.sexo}</span></div>
|
|
<div>Status: <span className="font-medium">{laudo.status}</span></div>
|
|
<div>CID: <span className="font-medium">{laudo.cid}</span></div>
|
|
<div>Data: <span className="font-medium">{laudo.data}</span></div>
|
|
</div>
|
|
{laudo.assinatura && (
|
|
<div className="mb-2">
|
|
<p className="font-semibold text-primary text-sm mb-1">Assinatura Digital:</p>
|
|
<img src={laudo.assinatura} alt="Assinatura digital" className="rounded-lg max-h-16 border bg-white shadow" />
|
|
</div>
|
|
)}
|
|
{laudo.imagem && (
|
|
<div className="mb-2">
|
|
<p className="font-semibold text-primary text-sm mb-1">Imagem:</p>
|
|
<img src={laudo.imagem} alt="Imagem do laudo" className="rounded-lg max-h-20 border border-primary/20 mb-1" />
|
|
</div>
|
|
)}
|
|
<div className="mb-1">
|
|
<p className="font-semibold text-primary text-sm mb-1">Conteúdo:</p>
|
|
<div className="text-sm text-gray-800" dangerouslySetInnerHTML={{ __html: laudo.conteudo }} />
|
|
</div>
|
|
</div>
|
|
))
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
|
|
const renderComunicacaoSection = () => (
|
|
<div className="bg-card shadow-md rounded-lg p-6">
|
|
<h2 className="text-2xl font-bold mb-4 text-foreground">Comunicação com o Paciente</h2>
|
|
<div className="space-y-6">
|
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
|
<div className="space-y-2">
|
|
<Label htmlFor="destinatario">Destinatário</Label>
|
|
<Input
|
|
id="destinatario"
|
|
placeholder="Nome do Paciente ou CPF"
|
|
disabled
|
|
className="bg-muted cursor-not-allowed text-muted-foreground disabled:text-foreground"
|
|
/>
|
|
</div>
|
|
<div className="space-y-2">
|
|
<Label htmlFor="tipoMensagem">Tipo de mensagem</Label>
|
|
<Select>
|
|
<SelectTrigger id="tipoMensagem" className="hover:border-primary focus:border-primary cursor-pointer">
|
|
<SelectValue placeholder="Selecione o tipo" />
|
|
</SelectTrigger>
|
|
<SelectContent className="bg-popover border">
|
|
<SelectItem value="lembrete" className="hover:bg-primary hover:text-primary-foreground focus:bg-primary focus:text-primary-foreground cursor-pointer">Lembrete de Consulta</SelectItem>
|
|
<SelectItem value="resultado" className="hover:bg-primary hover:text-primary-foreground focus:bg-primary focus:text-primary-foreground cursor-pointer">Resultado de Exame</SelectItem>
|
|
<SelectItem value="instrucao" className="hover:bg-primary hover:text-primary-foreground focus:bg-primary focus:text-primary-foreground cursor-pointer">Instruções Pós-Consulta</SelectItem>
|
|
<SelectItem value="outro" className="hover:bg-primary hover:text-primary-foreground focus:bg-primary focus:text-primary-foreground cursor-pointer">Outro</SelectItem>
|
|
</SelectContent>
|
|
</Select>
|
|
</div>
|
|
</div>
|
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
|
<div>
|
|
<Label htmlFor="dataEnvio">Data de envio</Label>
|
|
<p id="dataEnvio" className="text-sm text-muted-foreground">03/09/2025</p>
|
|
</div>
|
|
<div>
|
|
<Label htmlFor="statusEntrega">Status da entrega</Label>
|
|
<p id="statusEntrega" className="text-sm text-muted-foreground">Pendente</p>
|
|
</div>
|
|
</div>
|
|
<div className="space-y-2">
|
|
<Label>Resposta do paciente</Label>
|
|
<div className="border rounded-md p-3 bg-muted/40 space-y-2">
|
|
<p className="text-sm">"Ok, obrigado pelo lembrete!"</p>
|
|
<p className="text-xs text-muted-foreground">03/09/2025 14:30</p>
|
|
</div>
|
|
</div>
|
|
<div className="flex justify-end mt-6">
|
|
<Button onClick={handleSave}>Registrar Comunicação</Button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
|
|
// Função para renderizar a seção de relatórios médicos
|
|
const renderRelatoriosMedicosSection = () => (
|
|
<div className="space-y-6">
|
|
<div className="flex items-center justify-between">
|
|
<h2 className="text-2xl font-bold text-foreground">Relatórios Médicos</h2>
|
|
{editandoRelatorio && (
|
|
<Button variant="outline" onClick={handleCancelarEdicaoRelatorio}>
|
|
Cancelar Edição
|
|
</Button>
|
|
)}
|
|
</div>
|
|
|
|
{/* Formulário de Relatório Médico */}
|
|
<div className="bg-card shadow-md rounded-lg p-6">
|
|
<h3 className="text-lg font-semibold mb-4 text-foreground">
|
|
{editandoRelatorio ? 'Editar Relatório Médico' : 'Novo Relatório Médico'}
|
|
</h3>
|
|
|
|
<div className="grid gap-6">
|
|
{/* Identificação do Profissional */}
|
|
<div className="space-y-4">
|
|
<h4 className="text-md font-medium text-primary border-b pb-2">Identificação do Profissional</h4>
|
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
<div className="space-y-2">
|
|
<Label htmlFor="profissionalNome">Nome do Profissional</Label>
|
|
<Input
|
|
id="profissionalNome"
|
|
value={relatorioMedico.profissionalNome}
|
|
disabled
|
|
className="bg-muted"
|
|
/>
|
|
</div>
|
|
<div className="space-y-2">
|
|
<Label htmlFor="profissionalCrm">CRM e Especialidade</Label>
|
|
<Input
|
|
id="profissionalCrm"
|
|
value={relatorioMedico.profissionalCrm}
|
|
disabled
|
|
className="bg-muted"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Identificação do Paciente */}
|
|
<div className="space-y-4">
|
|
<h4 className="text-md font-medium text-primary border-b pb-2">Identificação do Paciente</h4>
|
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
|
<div className="space-y-2">
|
|
<Label htmlFor="pacienteNome">Nome do Paciente *</Label>
|
|
<Select
|
|
value={relatorioMedico.pacienteNome}
|
|
onValueChange={(value) => {
|
|
const pacienteSelecionado = pacientes.find(p => p.nome === value);
|
|
handleRelatorioChange('pacienteNome', value);
|
|
if (pacienteSelecionado) {
|
|
handleRelatorioChange('pacienteCpf', pacienteSelecionado.cpf);
|
|
handleRelatorioChange('pacienteIdade', pacienteSelecionado.idade.toString());
|
|
}
|
|
}}
|
|
>
|
|
<SelectTrigger>
|
|
<SelectValue placeholder="Selecione o paciente" />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
{pacientes.map((paciente) => (
|
|
<SelectItem key={paciente.cpf} value={paciente.nome}>
|
|
{paciente.nome}
|
|
</SelectItem>
|
|
))}
|
|
</SelectContent>
|
|
</Select>
|
|
</div>
|
|
<div className="space-y-2">
|
|
<Label htmlFor="pacienteCpf">CPF do Paciente</Label>
|
|
<Input
|
|
id="pacienteCpf"
|
|
value={relatorioMedico.pacienteCpf}
|
|
onChange={(e) => handleRelatorioChange('pacienteCpf', e.target.value)}
|
|
placeholder="000.000.000-00"
|
|
/>
|
|
</div>
|
|
<div className="space-y-2">
|
|
<Label htmlFor="pacienteIdade">Idade</Label>
|
|
<Input
|
|
id="pacienteIdade"
|
|
type="number"
|
|
value={relatorioMedico.pacienteIdade}
|
|
onChange={(e) => handleRelatorioChange('pacienteIdade', e.target.value)}
|
|
placeholder="Idade do paciente"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Informações do Relatório */}
|
|
<div className="space-y-4">
|
|
<h4 className="text-md font-medium text-primary border-b pb-2">Informações do Relatório</h4>
|
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
<div className="space-y-2">
|
|
<Label htmlFor="motivoRelatorio">Motivo do Relatório *</Label>
|
|
<Textarea
|
|
id="motivoRelatorio"
|
|
value={relatorioMedico.motivoRelatorio}
|
|
onChange={(e) => handleRelatorioChange('motivoRelatorio', e.target.value)}
|
|
placeholder="Descreva o motivo para a elaboração deste relatório médico..."
|
|
rows={3}
|
|
/>
|
|
</div>
|
|
<div className="space-y-2">
|
|
<Label htmlFor="dataRelatorio">Data do Relatório</Label>
|
|
<Input
|
|
id="dataRelatorio"
|
|
type="date"
|
|
value={relatorioMedico.dataRelatorio}
|
|
onChange={(e) => handleRelatorioChange('dataRelatorio', e.target.value)}
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="space-y-2">
|
|
<Label htmlFor="historicoClinico">Histórico Clínico Conciso</Label>
|
|
<Textarea
|
|
id="historicoClinico"
|
|
value={relatorioMedico.historicoClinico}
|
|
onChange={(e) => handleRelatorioChange('historicoClinico', e.target.value)}
|
|
placeholder="Resumo do histórico clínico relevante do paciente..."
|
|
rows={4}
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Sinais, Sintomas e Exames */}
|
|
<div className="space-y-4">
|
|
<h4 className="text-md font-medium text-primary border-b pb-2">Sinais, Sintomas e Exames</h4>
|
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
<div className="space-y-2">
|
|
<Label htmlFor="sinaisSintomas">Sinais e Sintomas</Label>
|
|
<Textarea
|
|
id="sinaisSintomas"
|
|
value={relatorioMedico.sinaisSintomas}
|
|
onChange={(e) => handleRelatorioChange('sinaisSintomas', e.target.value)}
|
|
placeholder="Descreva os sinais e sintomas observados..."
|
|
rows={4}
|
|
/>
|
|
</div>
|
|
<div className="space-y-2">
|
|
<Label htmlFor="examesRealizados">Exames Realizados</Label>
|
|
<Textarea
|
|
id="examesRealizados"
|
|
value={relatorioMedico.examesRealizados}
|
|
onChange={(e) => handleRelatorioChange('examesRealizados', e.target.value)}
|
|
placeholder="Liste os exames realizados..."
|
|
rows={4}
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="space-y-2">
|
|
<Label htmlFor="resultadosExames">Resultados Relevantes dos Exames</Label>
|
|
<Textarea
|
|
id="resultadosExames"
|
|
value={relatorioMedico.resultadosExames}
|
|
onChange={(e) => handleRelatorioChange('resultadosExames', e.target.value)}
|
|
placeholder="Descreva os resultados mais relevantes dos exames..."
|
|
rows={3}
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Diagnósticos e Prognóstico */}
|
|
<div className="space-y-4">
|
|
<h4 className="text-md font-medium text-primary border-b pb-2">Diagnósticos e Prognóstico</h4>
|
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
<div className="space-y-2">
|
|
<Label htmlFor="diagnosticos">Diagnóstico(s)</Label>
|
|
<Textarea
|
|
id="diagnosticos"
|
|
value={relatorioMedico.diagnosticos}
|
|
onChange={(e) => handleRelatorioChange('diagnosticos', e.target.value)}
|
|
placeholder="Informe o(s) diagnóstico(s) estabelecido(s)..."
|
|
rows={4}
|
|
/>
|
|
</div>
|
|
<div className="space-y-2">
|
|
<Label htmlFor="prognostico">Prognóstico (quando cabível)</Label>
|
|
<Textarea
|
|
id="prognostico"
|
|
value={relatorioMedico.prognostico}
|
|
onChange={(e) => handleRelatorioChange('prognostico', e.target.value)}
|
|
placeholder="Descreva o prognóstico, se aplicável..."
|
|
rows={4}
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Tratamentos e Recomendações */}
|
|
<div className="space-y-4">
|
|
<h4 className="text-md font-medium text-primary border-b pb-2">Tratamentos e Recomendações</h4>
|
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
<div className="space-y-2">
|
|
<Label htmlFor="tratamentosRealizados">Tratamentos já Realizados</Label>
|
|
<Textarea
|
|
id="tratamentosRealizados"
|
|
value={relatorioMedico.tratamentosRealizados}
|
|
onChange={(e) => handleRelatorioChange('tratamentosRealizados', e.target.value)}
|
|
placeholder="Descreva os tratamentos já realizados..."
|
|
rows={4}
|
|
/>
|
|
</div>
|
|
<div className="space-y-2">
|
|
<Label htmlFor="recomendacoes">Recomendações Objetivas</Label>
|
|
<Textarea
|
|
id="recomendacoes"
|
|
value={relatorioMedico.recomendacoes}
|
|
onChange={(e) => handleRelatorioChange('recomendacoes', e.target.value)}
|
|
placeholder="Informe as recomendações objetivas..."
|
|
rows={4}
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Botões de Ação */}
|
|
<div className="flex justify-end gap-4">
|
|
<Button variant="outline" onClick={handleCancelarEdicaoRelatorio}>
|
|
Cancelar
|
|
</Button>
|
|
<Button onClick={handleSalvarRelatorio} className="flex items-center gap-2">
|
|
<FileCheck className="h-4 w-4" />
|
|
{editandoRelatorio ? 'Atualizar Relatório' : 'Salvar Relatório'}
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Lista de Relatórios Existentes */}
|
|
<div className="bg-card shadow-md rounded-lg p-6">
|
|
<h3 className="text-lg font-semibold mb-4 text-foreground">Relatórios Médicos Salvos</h3>
|
|
|
|
{relatoriosMedicos.length === 0 ? (
|
|
<div className="text-center py-8 text-muted-foreground">
|
|
<FileCheck className="h-12 w-12 mx-auto mb-4 text-muted-foreground/50" />
|
|
<p className="text-lg mb-2">Nenhum relatório médico encontrado</p>
|
|
<p className="text-sm">Os relatórios salvos aparecerão aqui</p>
|
|
</div>
|
|
) : (
|
|
<div className="space-y-4">
|
|
{relatoriosMedicos.map((relatorio) => (
|
|
<div key={relatorio.id} className="border rounded-lg p-4 hover:shadow-md transition-shadow">
|
|
<div className="flex justify-between items-start mb-3">
|
|
<div>
|
|
<h4 className="font-semibold text-lg">{relatorio.pacienteNome}</h4>
|
|
<p className="text-sm text-muted-foreground">CPF: {relatorio.pacienteCpf} • Idade: {relatorio.pacienteIdade} anos</p>
|
|
<p className="text-sm text-muted-foreground">Data do relatório: {new Date(relatorio.dataRelatorio).toLocaleDateString('pt-BR')}</p>
|
|
<p className="text-xs text-muted-foreground/70">Gerado em: {relatorio.dataGeracao}</p>
|
|
</div>
|
|
<div className="flex gap-2">
|
|
<Button
|
|
variant="outline"
|
|
size="sm"
|
|
onClick={() => handleEditarRelatorio(relatorio)}
|
|
className="flex items-center gap-1"
|
|
>
|
|
<Edit className="h-3 w-3" />
|
|
Editar
|
|
</Button>
|
|
<Button
|
|
variant="destructive"
|
|
size="sm"
|
|
onClick={() => handleExcluirRelatorio(relatorio.id)}
|
|
className="flex items-center gap-1"
|
|
>
|
|
<Trash2 className="h-3 w-3" />
|
|
Excluir
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 text-sm">
|
|
<div>
|
|
<span className="font-medium text-primary">Motivo:</span>
|
|
<p className="text-foreground mt-1">{relatorio.motivoRelatorio}</p>
|
|
</div>
|
|
|
|
{relatorio.diagnosticos && (
|
|
<div>
|
|
<span className="font-medium text-primary">Diagnóstico(s):</span>
|
|
<p className="text-foreground mt-1">{relatorio.diagnosticos}</p>
|
|
</div>
|
|
)}
|
|
|
|
{relatorio.recomendacoes && (
|
|
<div className="md:col-span-2">
|
|
<span className="font-medium text-primary">Recomendações:</span>
|
|
<p className="text-foreground mt-1">{relatorio.recomendacoes}</p>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
);
|
|
|
|
const renderPerfilSection = () => (
|
|
<div className="space-y-6">
|
|
<div className="flex items-center justify-between">
|
|
<h2 className="text-2xl font-bold text-foreground">Meu Perfil</h2>
|
|
{!isEditingProfile ? (
|
|
<Button onClick={() => setIsEditingProfile(true)} className="flex items-center gap-2">
|
|
<Edit className="h-4 w-4" />
|
|
Editar Perfil
|
|
</Button>
|
|
) : (
|
|
<div className="flex gap-2">
|
|
<Button onClick={handleSaveProfile} className="flex items-center gap-2">
|
|
Salvar
|
|
</Button>
|
|
<Button variant="outline" onClick={handleCancelEdit}>
|
|
Cancelar
|
|
</Button>
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
<div className="grid gap-6 md:grid-cols-2">
|
|
{/* Informações Pessoais */}
|
|
<div className="space-y-4">
|
|
<h3 className="text-lg font-semibold border-b border-border text-foreground pb-2">Informações Pessoais</h3>
|
|
|
|
<div className="space-y-2">
|
|
<Label htmlFor="nome">Nome Completo</Label>
|
|
<p className="p-2 bg-muted rounded text-muted-foreground">{profileData.nome}</p>
|
|
<span className="text-xs text-muted-foreground">Este campo não pode ser alterado</span>
|
|
</div>
|
|
|
|
<div className="space-y-2">
|
|
<Label htmlFor="email">Email</Label>
|
|
{isEditingProfile ? (
|
|
<Input
|
|
id="email"
|
|
type="email"
|
|
value={profileData.email}
|
|
onChange={(e) => handleProfileChange('email', e.target.value)}
|
|
/>
|
|
) : (
|
|
<p className="p-2 bg-muted/50 rounded text-foreground">{profileData.email}</p>
|
|
)}
|
|
</div>
|
|
|
|
<div className="space-y-2">
|
|
<Label htmlFor="telefone">Telefone</Label>
|
|
{isEditingProfile ? (
|
|
<Input
|
|
id="telefone"
|
|
value={profileData.telefone}
|
|
onChange={(e) => handleProfileChange('telefone', e.target.value)}
|
|
/>
|
|
) : (
|
|
<p className="p-2 bg-muted/50 rounded text-foreground">{profileData.telefone}</p>
|
|
)}
|
|
</div>
|
|
|
|
<div className="space-y-2">
|
|
<Label htmlFor="crm">CRM</Label>
|
|
<p className="p-2 bg-muted rounded text-muted-foreground">{profileData.crm}</p>
|
|
<span className="text-xs text-muted-foreground">Este campo não pode ser alterado</span>
|
|
</div>
|
|
|
|
<div className="space-y-2">
|
|
<Label htmlFor="especialidade">Especialidade</Label>
|
|
{isEditingProfile ? (
|
|
<Input
|
|
id="especialidade"
|
|
value={profileData.especialidade}
|
|
onChange={(e) => handleProfileChange('especialidade', e.target.value)}
|
|
/>
|
|
) : (
|
|
<p className="p-2 bg-muted/50 rounded text-foreground">{profileData.especialidade}</p>
|
|
)}
|
|
</div>
|
|
</div>
|
|
|
|
{/* Endereço e Contato */}
|
|
<div className="space-y-4">
|
|
<h3 className="text-lg font-semibold border-b border-border text-foreground pb-2">Endereço e Contato</h3>
|
|
|
|
<div className="space-y-2">
|
|
<Label htmlFor="endereco">Endereço</Label>
|
|
{isEditingProfile ? (
|
|
<Input
|
|
id="endereco"
|
|
value={profileData.endereco}
|
|
onChange={(e) => handleProfileChange('endereco', e.target.value)}
|
|
/>
|
|
) : (
|
|
<p className="p-2 bg-muted/50 rounded text-foreground">{profileData.endereco}</p>
|
|
)}
|
|
</div>
|
|
|
|
<div className="space-y-2">
|
|
<Label htmlFor="cidade">Cidade</Label>
|
|
{isEditingProfile ? (
|
|
<Input
|
|
id="cidade"
|
|
value={profileData.cidade}
|
|
onChange={(e) => handleProfileChange('cidade', e.target.value)}
|
|
/>
|
|
) : (
|
|
<p className="p-2 bg-muted/50 rounded text-foreground">{profileData.cidade}</p>
|
|
)}
|
|
</div>
|
|
|
|
<div className="space-y-2">
|
|
<Label htmlFor="cep">CEP</Label>
|
|
{isEditingProfile ? (
|
|
<Input
|
|
id="cep"
|
|
value={profileData.cep}
|
|
onChange={(e) => handleProfileChange('cep', e.target.value)}
|
|
/>
|
|
) : (
|
|
<p className="p-2 bg-muted/50 rounded text-foreground">{profileData.cep}</p>
|
|
)}
|
|
</div>
|
|
|
|
<div className="space-y-2">
|
|
<Label htmlFor="biografia">Biografia</Label>
|
|
{isEditingProfile ? (
|
|
<Textarea
|
|
id="biografia"
|
|
value={profileData.biografia}
|
|
onChange={(e) => handleProfileChange('biografia', e.target.value)}
|
|
rows={4}
|
|
placeholder="Descreva sua experiência profissional..."
|
|
/>
|
|
) : (
|
|
<p className="p-2 bg-muted/50 rounded min-h-[100px] text-foreground">{profileData.biografia}</p>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Foto do Perfil */}
|
|
<div className="border-t border-border pt-6">
|
|
<h3 className="text-lg font-semibold mb-4 text-foreground">Foto do Perfil</h3>
|
|
<div className="flex items-center gap-4">
|
|
<Avatar className="h-20 w-20">
|
|
<AvatarFallback className="text-lg">
|
|
{profileData.nome.split(' ').map(n => n[0]).join('').toUpperCase()}
|
|
</AvatarFallback>
|
|
</Avatar>
|
|
{isEditingProfile && (
|
|
<div className="space-y-2">
|
|
<Button variant="outline" size="sm">
|
|
Alterar Foto
|
|
</Button>
|
|
<p className="text-xs text-muted-foreground">
|
|
Formatos aceitos: JPG, PNG (máx. 2MB)
|
|
</p>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
|
|
|
|
const renderActiveSection = () => {
|
|
switch (activeSection) {
|
|
case 'calendario':
|
|
return renderCalendarioSection();
|
|
case 'pacientes':
|
|
return <PacientesSection handleAbrirProntuario={handleAbrirProntuario} setActiveSection={setActiveSection} />;
|
|
case 'prontuario':
|
|
return renderProntuarioSection();
|
|
case 'laudos':
|
|
return renderLaudosSection();
|
|
case 'comunicacao':
|
|
return renderComunicacaoSection();
|
|
case 'relatorios-medicos':
|
|
return renderRelatoriosMedicosSection();
|
|
case 'perfil':
|
|
return renderPerfilSection();
|
|
default:
|
|
return renderCalendarioSection();
|
|
}
|
|
};
|
|
|
|
return (
|
|
<ProtectedRoute requiredUserType={["profissional"]}>
|
|
<div className="container mx-auto px-4 py-8">
|
|
<header className="bg-card shadow-md rounded-lg p-4 mb-6 flex items-center justify-between">
|
|
<div className="flex items-center gap-4">
|
|
<Avatar className="h-12 w-12">
|
|
<AvatarImage src={medico.fotoUrl} alt={medico.nome} />
|
|
<AvatarFallback className="bg-muted">
|
|
<User className="h-5 w-5" />
|
|
</AvatarFallback>
|
|
</Avatar>
|
|
<div className="min-w-0">
|
|
<p className="text-sm text-muted-foreground truncate">Conta do profissional</p>
|
|
<h2 className="text-lg font-semibold leading-none truncate">{medico.nome}</h2>
|
|
<p className="text-sm text-muted-foreground truncate">{medico.identificacao}</p>
|
|
{user?.email && (
|
|
<p className="text-xs text-muted-foreground truncate">Logado como: {user.email}</p>
|
|
)}
|
|
</div>
|
|
</div>
|
|
<Button
|
|
variant="outline"
|
|
onClick={logout}
|
|
className="text-red-600 border-red-600 hover:bg-red-600 hover:text-white cursor-pointer"
|
|
>
|
|
Sair
|
|
</Button>
|
|
</header>
|
|
|
|
<div className="grid grid-cols-1 md:grid-cols-[220px_1fr] gap-6">
|
|
{}
|
|
<aside className="md:sticky md:top-8 h-fit">
|
|
<nav className="bg-card shadow-md rounded-lg p-3 space-y-1">
|
|
<Button
|
|
variant={activeSection === 'calendario' ? 'default' : 'ghost'}
|
|
className="w-full justify-start hover:bg-primary hover:text-primary-foreground cursor-pointer"
|
|
onClick={() => setActiveSection('calendario')}
|
|
>
|
|
<CalendarIcon className="mr-2 h-4 w-4" />
|
|
Calendário
|
|
</Button>
|
|
<Button
|
|
variant={activeSection === 'pacientes' ? 'default' : 'ghost'}
|
|
className="w-full justify-start hover:bg-primary hover:text-primary-foreground cursor-pointer"
|
|
onClick={() => setActiveSection('pacientes')}
|
|
>
|
|
<Users className="mr-2 h-4 w-4" />
|
|
Pacientes
|
|
</Button>
|
|
<Button
|
|
variant={activeSection === 'prontuario' ? 'default' : 'ghost'}
|
|
className="w-full justify-start hover:bg-primary hover:text-primary-foreground cursor-pointer"
|
|
onClick={() => setActiveSection('prontuario')}
|
|
>
|
|
<ClipboardList className="mr-2 h-4 w-4" />
|
|
Prontuário
|
|
</Button>
|
|
<Button
|
|
variant={activeSection === 'laudos' ? 'default' : 'ghost'}
|
|
className="w-full justify-start hover:bg-primary hover:text-primary-foreground cursor-pointer"
|
|
onClick={() => setActiveSection('laudos')}
|
|
>
|
|
<FileText className="mr-2 h-4 w-4" />
|
|
Laudos
|
|
</Button>
|
|
<Button
|
|
variant={activeSection === 'comunicacao' ? 'default' : 'ghost'}
|
|
className="w-full justify-start hover:bg-primary hover:text-primary-foreground cursor-pointer"
|
|
onClick={() => setActiveSection('comunicacao')}
|
|
>
|
|
<MessageSquare className="mr-2 h-4 w-4" />
|
|
Comunicação
|
|
</Button>
|
|
<Button
|
|
variant={activeSection === 'relatorios-medicos' ? 'default' : 'ghost'}
|
|
className="w-full justify-start hover:bg-primary hover:text-primary-foreground cursor-pointer"
|
|
onClick={() => setActiveSection('relatorios-medicos')}
|
|
>
|
|
<FileCheck className="mr-2 h-4 w-4" />
|
|
Relatórios Médicos
|
|
</Button>
|
|
<Button
|
|
variant={activeSection === 'perfil' ? 'default' : 'ghost'}
|
|
className="w-full justify-start cursor-pointer hover:bg-primary hover:text-primary-foreground cursor-pointer"
|
|
onClick={() => setActiveSection('perfil')}
|
|
>
|
|
<Settings className="mr-2 h-4 w-4" />
|
|
Meu Perfil
|
|
</Button>
|
|
</nav>
|
|
</aside>
|
|
|
|
<main>
|
|
<div className="flex justify-between items-center mb-4">
|
|
<h1 className="text-3xl font-bold">Área do Profissional de Saúde</h1>
|
|
<Button asChild>
|
|
<Link href="/">Início</Link>
|
|
</Button>
|
|
|
|
</div>
|
|
<p className="mb-8">Bem-vindo à sua área exclusiva.</p>
|
|
|
|
{renderActiveSection()}
|
|
</main>
|
|
</div>
|
|
|
|
{}
|
|
{showPopup && (
|
|
<div className="fixed inset-0 bg-black/50 backdrop-blur-sm flex justify-center items-center z-50">
|
|
|
|
<div className="bg-card p-6 rounded-lg w-96 border">
|
|
|
|
{step === 1 && (
|
|
<>
|
|
<h3 className="text-lg font-semibold mb-2">Selecionar Paciente</h3>
|
|
<p className="text-sm text-gray-600 mb-4">
|
|
Data: {selectedDate ? new Date(selectedDate + 'T00:00:00').toLocaleDateString('pt-BR') : 'Não selecionada'}
|
|
</p>
|
|
<Select
|
|
value={newEvent.title}
|
|
onValueChange={(value) => setNewEvent({ ...newEvent, title: value })}
|
|
>
|
|
<SelectTrigger>
|
|
<SelectValue placeholder="Selecione o paciente" />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
{pacientes.map((paciente) => (
|
|
<SelectItem key={paciente.cpf} value={paciente.nome}>
|
|
{paciente.nome} - {paciente.cpf}
|
|
</SelectItem>
|
|
))}
|
|
</SelectContent>
|
|
</Select>
|
|
<div className="flex gap-2 mt-4">
|
|
<Button
|
|
onClick={() => setShowPopup(false)}
|
|
variant="outline"
|
|
className="flex-1"
|
|
>
|
|
Cancelar
|
|
</Button>
|
|
<Button
|
|
onClick={handleNextStep}
|
|
disabled={!newEvent.title}
|
|
className="flex-1"
|
|
>
|
|
Próximo
|
|
</Button>
|
|
</div>
|
|
</>
|
|
)}
|
|
|
|
{step === 2 && (
|
|
<>
|
|
<h3 className="text-lg font-semibold mb-4">Tipo da Consulta</h3>
|
|
<Select
|
|
value={newEvent.type}
|
|
onValueChange={(value) => setNewEvent({ ...newEvent, type: value })}
|
|
>
|
|
<SelectTrigger>
|
|
<SelectValue placeholder="Selecione o tipo" />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
{Object.keys(colorsByType).map((type) => (
|
|
<SelectItem key={type} value={type}>
|
|
{type}
|
|
</SelectItem>
|
|
))}
|
|
</SelectContent>
|
|
</Select>
|
|
<div className="flex gap-2 mt-4">
|
|
<Button
|
|
onClick={() => setStep(1)}
|
|
variant="outline"
|
|
className="flex-1"
|
|
>
|
|
Voltar
|
|
</Button>
|
|
<Button
|
|
onClick={handleNextStep}
|
|
disabled={!newEvent.type}
|
|
className="flex-1"
|
|
>
|
|
Próximo
|
|
</Button>
|
|
</div>
|
|
</>
|
|
)}
|
|
|
|
{step === 3 && (
|
|
<>
|
|
<h3 className="text-lg font-semibold mb-4">Horário da Consulta</h3>
|
|
<Input
|
|
type="time"
|
|
value={newEvent.time}
|
|
onChange={(e) => setNewEvent({ ...newEvent, time: e.target.value })}
|
|
className="mb-4"
|
|
/>
|
|
<div className="flex gap-2">
|
|
<Button
|
|
onClick={() => setStep(2)}
|
|
variant="outline"
|
|
className="flex-1"
|
|
>
|
|
Voltar
|
|
</Button>
|
|
<Button
|
|
onClick={handleNextStep}
|
|
disabled={!newEvent.time}
|
|
className="flex-1"
|
|
>
|
|
{editingEvent ? "Salvar" : "Agendar"}
|
|
</Button>
|
|
</div>
|
|
</>
|
|
)}
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{}
|
|
{showActionModal && selectedEvent && (
|
|
<div className="fixed inset-0 bg-black bg-opacity-50 flex justify-center items-center z-50">
|
|
<div className="bg-card p-6 rounded-lg w-96">
|
|
<h3 className="text-lg font-semibold mb-2">
|
|
Consulta de {selectedEvent.title}
|
|
</h3>
|
|
<p className="text-sm text-gray-600 mb-4">
|
|
{selectedEvent.extendedProps.type} às {selectedEvent.extendedProps.time}
|
|
</p>
|
|
|
|
<div className="flex gap-2">
|
|
<Button
|
|
onClick={handleStartEdit}
|
|
className="flex-1 flex items-center gap-2"
|
|
>
|
|
<Edit className="h-4 w-4" />
|
|
Editar
|
|
</Button>
|
|
<Button
|
|
onClick={handleDeleteEvent}
|
|
variant="destructive"
|
|
className="flex-1 flex items-center gap-2"
|
|
>
|
|
<Trash2 className="h-4 w-4" />
|
|
Excluir
|
|
</Button>
|
|
</div>
|
|
|
|
<Button
|
|
onClick={() => setShowActionModal(false)}
|
|
variant="outline"
|
|
className="w-full mt-2"
|
|
>
|
|
Cancelar
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{/* Modal de Visualização do Laudo */}
|
|
<Dialog open={!!laudoVisualizacao} onOpenChange={() => setLaudoVisualizacao(null)}>
|
|
<DialogContent className="max-w-4xl max-h-[90vh] overflow-y-auto">
|
|
<DialogHeader>
|
|
<DialogTitle>
|
|
{laudoVisualizacao?.exameTipo || "Laudo Médico"}
|
|
</DialogTitle>
|
|
</DialogHeader>
|
|
|
|
{laudoVisualizacao && (
|
|
<div className="space-y-6">
|
|
{/* Cabeçalho do Laudo */}
|
|
<div className="text-center border-b pb-4">
|
|
<h1 className="text-2xl font-bold mb-2">LAUDO MÉDICO</h1>
|
|
<p className="text-gray-600">Data: {laudoVisualizacao.data}</p>
|
|
</div>
|
|
|
|
{/* Dados do Paciente */}
|
|
<div className="grid grid-cols-2 gap-4 p-4 bg-gray-50 rounded">
|
|
<div><strong>Paciente:</strong> {laudoVisualizacao.recipient}</div>
|
|
<div><strong>Executante:</strong> {laudoVisualizacao.execSolicitante}</div>
|
|
<div><strong>Exame:</strong> {laudoVisualizacao.exameTipo}</div>
|
|
{laudoVisualizacao.cid && (
|
|
<div><strong>CID:</strong> {laudoVisualizacao.cid}</div>
|
|
)}
|
|
</div>
|
|
|
|
{/* Diagnóstico */}
|
|
{laudoVisualizacao.diagnostico && (
|
|
<div className="space-y-2">
|
|
<h3 className="font-bold text-lg">DIAGNÓSTICO:</h3>
|
|
<p className="text-gray-800">{laudoVisualizacao.diagnostico}</p>
|
|
</div>
|
|
)}
|
|
|
|
{/* Conteúdo do Laudo */}
|
|
{laudoVisualizacao.conteudo && (
|
|
<div className="space-y-2">
|
|
<h3 className="font-bold text-lg">RELATÓRIO:</h3>
|
|
<div
|
|
className="prose max-w-none leading-relaxed text-gray-800"
|
|
dangerouslySetInnerHTML={{
|
|
__html: formatTextToHtml(laudoVisualizacao.conteudo)
|
|
}}
|
|
/>
|
|
</div>
|
|
)}
|
|
|
|
{/* Conclusão */}
|
|
{laudoVisualizacao.conclusao && (
|
|
<div className="space-y-2">
|
|
<h3 className="font-bold text-lg">CONCLUSÃO:</h3>
|
|
<p className="text-gray-800">{laudoVisualizacao.conclusao}</p>
|
|
</div>
|
|
)}
|
|
|
|
{/* Rodapé */}
|
|
<div className="border-t pt-4 text-center text-sm text-gray-600">
|
|
<p><strong>Status:</strong> {laudoVisualizacao.status}</p>
|
|
<p className="mt-2">
|
|
<strong>{medico.nome}</strong><br/>
|
|
{medico.identificacao}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
)}
|
|
</DialogContent>
|
|
</Dialog>
|
|
|
|
</div>
|
|
</ProtectedRoute>
|
|
);
|
|
};
|
|
|
|
export default ProfissionalPage; |