J.R.
This commit is contained in:
parent
b9b49cba42
commit
8a63219cf6
@ -106,10 +106,6 @@ export default function NovoLaudoPage() {
|
||||
<CardContent>
|
||||
<form onSubmit={handleSubmit} className="space-y-6">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="order_number">Nº do Pedido</Label>
|
||||
<Input id="order_number" value={formData.order_number} onChange={handleInputChange} />
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="exam">Exame</Label>
|
||||
<Input id="exam" value={formData.exam} onChange={handleInputChange} />
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
import { useEffect, useState, useCallback } from "react";
|
||||
import Link from "next/link";
|
||||
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@/components/ui/dropdown-menu";
|
||||
import { Eye, Edit, Calendar, Trash2, Loader2 } from "lucide-react";
|
||||
import { Eye, Edit, Calendar, Trash2, Loader2, MoreVertical } from "lucide-react";
|
||||
import { api } from "@/services/api.mjs";
|
||||
import { PatientDetailsModal } from "@/components/ui/patient-details-modal";
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
||||
@ -185,7 +185,7 @@ export default function PacientesPage() {
|
||||
<SelectItem value="20">20 por página</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<Link href="/doctor/pacientes/novo" className="w-full sm:w-auto">
|
||||
<Link href="/doctor/medicos/novo" className="w-full sm:w-auto">
|
||||
<Button variant="default" className="bg-green-600 hover:bg-green-700 w-full sm:w-auto">
|
||||
Novo Paciente
|
||||
</Button>
|
||||
@ -262,9 +262,10 @@ export default function PacientesPage() {
|
||||
<td className="p-3 sm:p-4">
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<button className="text-primary hover:underline text-sm sm:text-base">
|
||||
Ações
|
||||
</button>
|
||||
<Button variant="ghost" className="h-8 w-8 p-0">
|
||||
<span className="sr-only">Abrir menu</span>
|
||||
<MoreVertical className="h-4 w-4" />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end">
|
||||
<DropdownMenuItem onClick={() => handleOpenModal(p)}>
|
||||
@ -272,16 +273,16 @@ export default function PacientesPage() {
|
||||
Ver detalhes
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem asChild>
|
||||
<Link href={`/doctor/pacientes/${p.id}/laudos`}>
|
||||
<Link href={`/doctor/medicos/${p.id}/laudos`}>
|
||||
<Edit className="w-4 h-4 mr-2" />
|
||||
Laudos
|
||||
</Link>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem onClick={() => alert(`Agenda para paciente ID: ${p.id}`)}>
|
||||
{/* <DropdownMenuItem onClick={() => alert(`Agenda para paciente ID: ${p.id}`)}>
|
||||
<Calendar className="w-4 h-4 mr-2" />
|
||||
Ver agenda
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem
|
||||
</DropdownMenuItem> */}
|
||||
{/* <DropdownMenuItem
|
||||
onClick={() => {
|
||||
const newPacientes = pacientes.filter((pac) => pac.id !== p.id);
|
||||
setPacientes(newPacientes);
|
||||
@ -291,7 +292,7 @@ export default function PacientesPage() {
|
||||
>
|
||||
<Trash2 className="w-4 h-4 mr-2" />
|
||||
Excluir
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuItem> */}
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</td>
|
||||
@ -387,8 +388,7 @@ export default function PacientesPage() {
|
||||
<button
|
||||
key={number}
|
||||
onClick={() => paginate(number)}
|
||||
className={`px-4 py-2 rounded-md font-medium transition-colors text-sm border border-border ${
|
||||
currentPage === number
|
||||
className={`px-4 py-2 rounded-md font-medium transition-colors text-sm border border-border ${currentPage === number
|
||||
? "bg-green-600 text-primary-foreground shadow-md border-green-600"
|
||||
: "bg-secondary text-secondary-foreground hover:bg-secondary/80"
|
||||
}`}
|
||||
|
||||
@ -6,7 +6,7 @@ import { useRouter } from "next/navigation";
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@/components/ui/dropdown-menu"
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"
|
||||
import { Edit, Trash2, Eye, Calendar, Filter, Loader2 } from "lucide-react"
|
||||
import { Edit, Trash2, Eye, Calendar, Filter, Loader2, MoreVertical } from "lucide-react"
|
||||
import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle } from "@/components/ui/alert-dialog"
|
||||
|
||||
import { doctorsService } from "services/doctorsApi.mjs";
|
||||
@ -284,7 +284,10 @@ export default function DoctorsPage() {
|
||||
<td className="px-4 py-3 text-right">
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<div className="text-blue-600 cursor-pointer inline-block">Ações</div>
|
||||
<Button variant="ghost" className="h-8 w-8 p-0">
|
||||
<span className="sr-only">Abrir menu</span>
|
||||
<MoreVertical className="h-4 w-4" />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end">
|
||||
<DropdownMenuItem onClick={() => openDetailsDialog(doctor)}>
|
||||
@ -342,7 +345,7 @@ export default function DoctorsPage() {
|
||||
</div>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<div className="text-blue-600 cursor-pointer inline-block">Ações</div>
|
||||
<div className="text-blue-600 cursor-pointer inline-block">A99ções</div>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end">
|
||||
<DropdownMenuItem onClick={() => openDetailsDialog(doctor)}>
|
||||
@ -386,8 +389,7 @@ export default function DoctorsPage() {
|
||||
<button
|
||||
key={number}
|
||||
onClick={() => paginate(number)}
|
||||
className={`px-4 py-2 rounded-md font-medium transition-colors text-sm border border-gray-300 ${
|
||||
currentPage === number
|
||||
className={`px-4 py-2 rounded-md font-medium transition-colors text-sm border border-gray-300 ${currentPage === number
|
||||
? "bg-green-600 text-white shadow-md border-green-600"
|
||||
: "bg-gray-100 text-gray-700 hover:bg-gray-200"
|
||||
}`}
|
||||
|
||||
@ -5,7 +5,7 @@ import Link from "next/link";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@/components/ui/dropdown-menu";
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
||||
import { Edit, Trash2, Eye, Calendar, Filter, Loader2 } from "lucide-react";
|
||||
import { Edit, Trash2, Eye, Calendar, Filter, Loader2, MoreVertical } from "lucide-react";
|
||||
import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle } from "@/components/ui/alert-dialog";
|
||||
import { patientsService } from "@/services/patientsApi.mjs";
|
||||
import Sidebar from "@/components/Sidebar";
|
||||
@ -264,7 +264,10 @@ export default function PacientesPage() {
|
||||
<td className="p-4">
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<div className="text-blue-600 cursor-pointer">Ações</div>
|
||||
<Button variant="ghost" className="h-8 w-8 p-0">
|
||||
<span className="sr-only">Abrir menu</span>
|
||||
<MoreVertical className="h-4 w-4" />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end">
|
||||
<DropdownMenuItem onClick={() => openDetailsDialog(String(patient.id))}>
|
||||
@ -328,7 +331,10 @@ export default function PacientesPage() {
|
||||
</div>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<div className="w-full"><Button variant="outline" className="w-full">Ações</Button></div>
|
||||
<Button variant="ghost" className="h-8 w-8 p-0">
|
||||
<span className="sr-only">Abrir menu</span>
|
||||
<MoreVertical className="h-4 w-4" />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end">
|
||||
<DropdownMenuItem onClick={() => openDetailsDialog(String(patient.id))}>
|
||||
|
||||
@ -1,159 +1,67 @@
|
||||
"use client"
|
||||
|
||||
import { useState, useEffect } from "react"
|
||||
import { useState, useEffect, useMemo } from "react"
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
|
||||
import { Badge } from "@/components/ui/badge"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from "@/components/ui/dialog"
|
||||
import { toast } from "@/hooks/use-toast"
|
||||
import { FileText, Download, Eye, Calendar, User, X } from "lucide-react"
|
||||
import { FileText, Download, Eye, Calendar, User, X, Loader2 } from "lucide-react"
|
||||
import Sidebar from "@/components/Sidebar"
|
||||
import { useAuthLayout } from "@/hooks/useAuthLayout"
|
||||
import { reportsApi } from "@/services/reportsApi.mjs"
|
||||
|
||||
interface Report {
|
||||
id: string
|
||||
title: string
|
||||
doctor: string
|
||||
date: string
|
||||
type: string
|
||||
status: "disponivel" | "pendente"
|
||||
description: string
|
||||
content: {
|
||||
patientInfo: {
|
||||
name: string
|
||||
age: number
|
||||
gender: string
|
||||
id: string
|
||||
}
|
||||
examDetails: {
|
||||
requestingDoctor: string
|
||||
examDate: string
|
||||
reportDate: string
|
||||
technique: string
|
||||
}
|
||||
findings: string
|
||||
conclusion: string
|
||||
recommendations?: string
|
||||
}
|
||||
id: string;
|
||||
order_number: string;
|
||||
patient_id: string;
|
||||
status: string;
|
||||
exam: string;
|
||||
requested_by: string;
|
||||
cid_code: string;
|
||||
diagnosis: string;
|
||||
conclusion: string;
|
||||
content_html: string;
|
||||
content_json: any;
|
||||
hide_date: boolean;
|
||||
hide_signature: boolean;
|
||||
due_at: string;
|
||||
created_by: string;
|
||||
updated_by: string;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
}
|
||||
|
||||
export default function ReportsPage() {
|
||||
const [reports, setReports] = useState<Report[]>([])
|
||||
const [selectedReport, setSelectedReport] = useState<Report | null>(null)
|
||||
const [isViewModalOpen, setIsViewModalOpen] = useState(false)
|
||||
const [isLoading, setIsLoading] = useState(true)
|
||||
const requiredRole = useMemo(() => ["paciente"], []);
|
||||
const { user, isLoading: isAuthLoading } = useAuthLayout({ requiredRole });
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
const mockReports: Report[] = [
|
||||
{
|
||||
id: "1",
|
||||
title: "Exame de Sangue - Hemograma Completo",
|
||||
doctor: "Dr. João Silva",
|
||||
date: "2024-01-15",
|
||||
type: "Exame Laboratorial",
|
||||
status: "disponivel",
|
||||
description: "Hemograma completo com contagem de células sanguíneas",
|
||||
content: {
|
||||
patientInfo: {
|
||||
name: "Maria Silva Santos",
|
||||
age: 35,
|
||||
gender: "Feminino",
|
||||
id: "123.456.789-00",
|
||||
},
|
||||
examDetails: {
|
||||
requestingDoctor: "Dr. João Silva - CRM 12345",
|
||||
examDate: "15/01/2024",
|
||||
reportDate: "15/01/2024",
|
||||
technique: "Análise automatizada com confirmação microscópica",
|
||||
},
|
||||
findings:
|
||||
"Hemácias: 4.5 milhões/mm³ (VR: 4.0-5.2)\nHemoglobina: 13.2 g/dL (VR: 12.0-15.5)\nHematócrito: 40% (VR: 36-46)\nLeucócitos: 7.200/mm³ (VR: 4.000-11.000)\nPlaquetas: 280.000/mm³ (VR: 150.000-450.000)\n\nFórmula leucocitária:\n- Neutrófilos: 65% (VR: 50-70%)\n- Linfócitos: 28% (VR: 20-40%)\n- Monócitos: 5% (VR: 2-8%)\n- Eosinófilos: 2% (VR: 1-4%)",
|
||||
conclusion:
|
||||
"Hemograma dentro dos parâmetros normais. Não foram observadas alterações significativas na série vermelha, branca ou plaquetária.",
|
||||
recommendations: "Manter acompanhamento médico regular. Repetir exame conforme orientação médica.",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "2",
|
||||
title: "Radiografia do Tórax",
|
||||
doctor: "Dra. Maria Santos",
|
||||
date: "2024-01-10",
|
||||
type: "Exame de Imagem",
|
||||
status: "disponivel",
|
||||
description: "Radiografia PA e perfil do tórax",
|
||||
content: {
|
||||
patientInfo: {
|
||||
name: "Maria Silva Santos",
|
||||
age: 35,
|
||||
gender: "Feminino",
|
||||
id: "123.456.789-00",
|
||||
},
|
||||
examDetails: {
|
||||
requestingDoctor: "Dra. Maria Santos - CRM 67890",
|
||||
examDate: "10/01/2024",
|
||||
reportDate: "10/01/2024",
|
||||
technique: "Radiografia digital PA e perfil",
|
||||
},
|
||||
findings:
|
||||
"Campos pulmonares livres, sem sinais de consolidação ou derrame pleural. Silhueta cardíaca dentro dos limites normais. Estruturas ósseas íntegras. Diafragmas em posição normal.",
|
||||
conclusion: "Radiografia de tórax sem alterações patológicas evidentes.",
|
||||
recommendations: "Correlacionar com quadro clínico. Acompanhamento conforme indicação médica.",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "3",
|
||||
title: "Eletrocardiograma",
|
||||
doctor: "Dr. Carlos Oliveira",
|
||||
date: "2024-01-08",
|
||||
type: "Exame Cardiológico",
|
||||
status: "pendente",
|
||||
description: "ECG de repouso para avaliação cardíaca",
|
||||
content: {
|
||||
patientInfo: {
|
||||
name: "Maria Silva Santos",
|
||||
age: 35,
|
||||
gender: "Feminino",
|
||||
id: "123.456.789-00",
|
||||
},
|
||||
examDetails: {
|
||||
requestingDoctor: "Dr. Carlos Oliveira - CRM 54321",
|
||||
examDate: "08/01/2024",
|
||||
reportDate: "",
|
||||
technique: "ECG de repouso",
|
||||
},
|
||||
findings: "",
|
||||
conclusion: "",
|
||||
recommendations: "",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "4",
|
||||
title: "Ultrassom Abdominal",
|
||||
doctor: "Dra. Ana Costa",
|
||||
date: "2024-01-05",
|
||||
type: "Exame de Imagem",
|
||||
status: "disponivel",
|
||||
description: "Ultrassonografia do abdome total",
|
||||
content: {
|
||||
patientInfo: {
|
||||
name: "Maria Silva Santos",
|
||||
age: 35,
|
||||
gender: "Feminino",
|
||||
id: "123.456.789-00",
|
||||
},
|
||||
examDetails: {
|
||||
requestingDoctor: "Dra. Ana Costa - CRM 98765",
|
||||
examDate: "05/01/2024",
|
||||
reportDate: "05/01/2024",
|
||||
technique: "Ultrassom convencional",
|
||||
},
|
||||
findings:
|
||||
"Viscerais bem posicionadas. Rim direito e esquerdo com contornos normais. Vesícula com volume dentro do normal.",
|
||||
conclusion: "Ultrassom abdominal sem alterações patológicas evidentes.",
|
||||
recommendations: "Acompanhamento conforme indicação médica.",
|
||||
},
|
||||
},
|
||||
]
|
||||
setReports(mockReports)
|
||||
}, [])
|
||||
if (user) {
|
||||
const fetchReports = async () => {
|
||||
try {
|
||||
setIsLoading(true);
|
||||
const fetchedReports = await reportsApi.getReports(user.id);
|
||||
setReports(fetchedReports);
|
||||
} catch (error) {
|
||||
console.error("Erro ao buscar laudos:", error)
|
||||
toast({
|
||||
title: "Erro ao buscar laudos",
|
||||
description: "Não foi possível carregar os laudos. Tente novamente.",
|
||||
variant: "destructive",
|
||||
})
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}
|
||||
fetchReports()
|
||||
}
|
||||
}, [user?.id])
|
||||
|
||||
const handleViewReport = (reportId: string) => {
|
||||
const report = reports.find((r) => r.id === reportId)
|
||||
@ -168,100 +76,23 @@ export default function ReportsPage() {
|
||||
if (!report) return
|
||||
|
||||
try {
|
||||
// Simular loading
|
||||
toast({
|
||||
title: "Preparando download...",
|
||||
description: "Gerando PDF do laudo médico",
|
||||
})
|
||||
|
||||
// Criar conteúdo HTML do laudo para conversão em PDF
|
||||
const htmlContent = `
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Laudo Médico - ${report.title}</title>
|
||||
<style>
|
||||
body { font-family: Arial, sans-serif; margin: 40px; line-height: 1.6; }
|
||||
.header { text-align: center; border-bottom: 2px solid #333; padding-bottom: 20px; margin-bottom: 30px; }
|
||||
.section { margin-bottom: 25px; }
|
||||
.section-title { font-size: 16px; font-weight: bold; color: #333; margin-bottom: 10px; border-bottom: 1px solid #ccc; padding-bottom: 5px; }
|
||||
.info-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 15px; margin-bottom: 15px; }
|
||||
.info-item { margin-bottom: 8px; }
|
||||
.label { font-weight: bold; color: #555; }
|
||||
.content { white-space: pre-line; }
|
||||
.footer { margin-top: 40px; text-align: center; font-size: 12px; color: #666; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="header">
|
||||
<h1>LAUDO MÉDICO</h1>
|
||||
<h2>${report.title}</h2>
|
||||
<p><strong>Tipo:</strong> ${report.type}</p>
|
||||
</div>
|
||||
const htmlContent = report.content_html;
|
||||
|
||||
<div class="section">
|
||||
<div class="section-title">DADOS DO PACIENTE</div>
|
||||
<div class="info-grid">
|
||||
<div class="info-item"><span class="label">Nome:</span> ${report.content.patientInfo.name}</div>
|
||||
<div class="info-item"><span class="label">Idade:</span> ${report.content.patientInfo.age} anos</div>
|
||||
<div class="info-item"><span class="label">Sexo:</span> ${report.content.patientInfo.gender}</div>
|
||||
<div class="info-item"><span class="label">CPF:</span> ${report.content.patientInfo.id}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<div class="section-title">DETALHES DO EXAME</div>
|
||||
<div class="info-grid">
|
||||
<div class="info-item"><span class="label">Médico Solicitante:</span> ${report.content.examDetails.requestingDoctor}</div>
|
||||
<div class="info-item"><span class="label">Data do Exame:</span> ${report.content.examDetails.examDate}</div>
|
||||
<div class="info-item"><span class="label">Data do Laudo:</span> ${report.content.examDetails.reportDate}</div>
|
||||
<div class="info-item"><span class="label">Técnica:</span> ${report.content.examDetails.technique}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<div class="section-title">ACHADOS</div>
|
||||
<div class="content">${report.content.findings}</div>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<div class="section-title">CONCLUSÃO</div>
|
||||
<div class="content">${report.content.conclusion}</div>
|
||||
</div>
|
||||
|
||||
${
|
||||
report.content.recommendations
|
||||
? `
|
||||
<div class="section">
|
||||
<div class="section-title">RECOMENDAÇÕES</div>
|
||||
<div class="content">${report.content.recommendations}</div>
|
||||
</div>
|
||||
`
|
||||
: ""
|
||||
}
|
||||
|
||||
<div class="footer">
|
||||
<p>Documento gerado em ${new Date().toLocaleDateString("pt-BR")} às ${new Date().toLocaleTimeString("pt-BR")}</p>
|
||||
<p>Este é um documento médico oficial. Mantenha-o em local seguro.</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
`
|
||||
|
||||
// Criar blob com o conteúdo HTML
|
||||
const blob = new Blob([htmlContent], { type: "text/html" })
|
||||
const url = URL.createObjectURL(blob)
|
||||
|
||||
// Criar link temporário para download
|
||||
const link = document.createElement("a")
|
||||
link.href = url
|
||||
link.download = `laudo-${report.title.replace(/[^a-zA-Z0-9]/g, "-").toLowerCase()}-${report.date}.html`
|
||||
link.download = `laudo-${report.order_number}.html`
|
||||
document.body.appendChild(link)
|
||||
link.click()
|
||||
document.body.removeChild(link)
|
||||
|
||||
// Limpar URL temporária
|
||||
URL.revokeObjectURL(url)
|
||||
|
||||
toast({
|
||||
@ -283,8 +114,18 @@ export default function ReportsPage() {
|
||||
setSelectedReport(null)
|
||||
}
|
||||
|
||||
const availableReports = reports.filter((report) => report.status === "disponivel")
|
||||
const pendingReports = reports.filter((report) => report.status === "pendente")
|
||||
const availableReports = reports.filter((report) => report.status.toLowerCase() === "draft")
|
||||
const pendingReports = reports.filter((report) => report.status.toLowerCase() !== "draft")
|
||||
|
||||
if (isLoading || isAuthLoading) {
|
||||
return (
|
||||
<Sidebar>
|
||||
<div className="flex justify-center items-center h-full">
|
||||
<Loader2 className="h-8 w-8 animate-spin" />
|
||||
</div>
|
||||
</Sidebar>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<Sidebar>
|
||||
@ -333,25 +174,25 @@ export default function ReportsPage() {
|
||||
<CardHeader>
|
||||
<div className="flex items-start justify-between">
|
||||
<div className="space-y-1">
|
||||
<CardTitle className="text-lg">{report.title}</CardTitle>
|
||||
<CardTitle className="text-lg">{report.exam}</CardTitle>
|
||||
<CardDescription className="flex items-center gap-4">
|
||||
<span className="flex items-center gap-1">
|
||||
<User className="h-4 w-4" />
|
||||
{report.doctor}
|
||||
{report.requested_by}
|
||||
</span>
|
||||
<span className="flex items-center gap-1">
|
||||
<Calendar className="h-4 w-4" />
|
||||
{new Date(report.date).toLocaleDateString("pt-BR")}
|
||||
{new Date(report.created_at).toLocaleDateString("pt-BR")}
|
||||
</span>
|
||||
</CardDescription>
|
||||
</div>
|
||||
<Badge variant="secondary" className="bg-green-100 text-green-800">
|
||||
{report.type}
|
||||
Finalizado
|
||||
</Badge>
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<p className="text-gray-600 mb-4">{report.description}</p>
|
||||
<p className="text-gray-600 mb-4">{report.diagnosis}</p>
|
||||
<div className="flex gap-2">
|
||||
<Button
|
||||
variant="outline"
|
||||
@ -369,7 +210,7 @@ export default function ReportsPage() {
|
||||
className="flex items-center gap-2"
|
||||
>
|
||||
<Download className="h-4 w-4" />
|
||||
Baixar PDF
|
||||
Baixar
|
||||
</Button>
|
||||
</div>
|
||||
</CardContent>
|
||||
@ -388,25 +229,25 @@ export default function ReportsPage() {
|
||||
<CardHeader>
|
||||
<div className="flex items-start justify-between">
|
||||
<div className="space-y-1">
|
||||
<CardTitle className="text-lg">{report.title}</CardTitle>
|
||||
<CardTitle className="text-lg">{report.exam}</CardTitle>
|
||||
<CardDescription className="flex items-center gap-4">
|
||||
<span className="flex items-center gap-1">
|
||||
<User className="h-4 w-4" />
|
||||
{report.doctor}
|
||||
{report.requested_by}
|
||||
</span>
|
||||
<span className="flex items-center gap-1">
|
||||
<Calendar className="h-4 w-4" />
|
||||
{new Date(report.date).toLocaleDateString("pt-BR")}
|
||||
{new Date(report.created_at).toLocaleDateString("pt-BR")}
|
||||
</span>
|
||||
</CardDescription>
|
||||
</div>
|
||||
<Badge variant="secondary" className="bg-yellow-100 text-yellow-800">
|
||||
Pendente
|
||||
{report.status}
|
||||
</Badge>
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<p className="text-gray-600 mb-4">{report.description}</p>
|
||||
<p className="text-gray-600 mb-4">{report.diagnosis}</p>
|
||||
<p className="text-sm text-yellow-600 font-medium">
|
||||
Laudo em processamento. Você será notificado quando estiver disponível.
|
||||
</p>
|
||||
@ -417,7 +258,7 @@ export default function ReportsPage() {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{reports.length === 0 && (
|
||||
{reports.length === 0 && !isLoading && (
|
||||
<Card className="text-center py-12">
|
||||
<CardContent>
|
||||
<FileText className="h-12 w-12 text-gray-400 mx-auto mb-4" />
|
||||
@ -432,9 +273,9 @@ export default function ReportsPage() {
|
||||
<DialogHeader>
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<DialogTitle className="text-xl font-bold">{selectedReport?.title}</DialogTitle>
|
||||
<DialogTitle className="text-xl font-bold">{selectedReport?.exam}</DialogTitle>
|
||||
<DialogDescription className="mt-1">
|
||||
{selectedReport?.type} - {selectedReport?.doctor}
|
||||
{selectedReport?.order_number}
|
||||
</DialogDescription>
|
||||
</div>
|
||||
<Button variant="ghost" size="sm" onClick={handleCloseModal} className="h-8 w-8 p-0">
|
||||
@ -444,94 +285,7 @@ export default function ReportsPage() {
|
||||
</DialogHeader>
|
||||
|
||||
{selectedReport && (
|
||||
<div className="space-y-6 mt-4">
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle className="text-lg">Dados do Paciente</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="grid grid-cols-2 gap-4">
|
||||
<div>
|
||||
<p className="text-sm font-medium text-gray-500">Nome</p>
|
||||
<p className="text-sm">{selectedReport.content.patientInfo.name}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm font-medium text-gray-500">Idade</p>
|
||||
<p className="text-sm">{selectedReport.content.patientInfo.age} anos</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm font-medium text-gray-500">Sexo</p>
|
||||
<p className="text-sm">{selectedReport.content.patientInfo.gender}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm font-medium text-gray-500">CPF</p>
|
||||
<p className="text-sm">{selectedReport.content.patientInfo.id}</p>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle className="text-lg">Detalhes do Exame</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="grid grid-cols-2 gap-4">
|
||||
<div>
|
||||
<p className="text-sm font-medium text-gray-500">Médico Solicitante</p>
|
||||
<p className="text-sm">{selectedReport.content.examDetails.requestingDoctor}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm font-medium text-gray-500">Data do Exame</p>
|
||||
<p className="text-sm">{selectedReport.content.examDetails.examDate}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm font-medium text-gray-500">Data do Laudo</p>
|
||||
<p className="text-sm">{selectedReport.content.examDetails.reportDate}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm font-medium text-gray-500">Técnica</p>
|
||||
<p className="text-sm">{selectedReport.content.examDetails.technique}</p>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle className="text-lg">Achados</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="whitespace-pre-line text-sm leading-relaxed">{selectedReport.content.findings}</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle className="text-lg">Conclusão</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<p className="text-sm leading-relaxed">{selectedReport.content.conclusion}</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{selectedReport.content.recommendations && (
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle className="text-lg">Recomendações</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<p className="text-sm leading-relaxed">{selectedReport.content.recommendations}</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)}
|
||||
|
||||
<div className="flex gap-3 pt-4 border-t">
|
||||
<Button onClick={() => handleDownloadReport(selectedReport.id)} className="flex items-center gap-2">
|
||||
<Download className="h-4 w-4" />
|
||||
Baixar PDF
|
||||
</Button>
|
||||
<Button variant="outline" onClick={handleCloseModal}>
|
||||
Fechar
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="space-y-6 mt-4" dangerouslySetInnerHTML={{ __html: selectedReport.content_html }} />
|
||||
)}
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
|
||||
@ -6,7 +6,7 @@ import Link from "next/link";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@/components/ui/dropdown-menu";
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
||||
import { Plus, Edit, Trash2, Eye, Calendar, Filter, Loader2 } from "lucide-react";
|
||||
import { Plus, Edit, Trash2, Eye, Calendar, Filter, Loader2, MoreVertical } from "lucide-react";
|
||||
import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle } from "@/components/ui/alert-dialog";
|
||||
import { patientsService } from "@/services/patientsApi.mjs";
|
||||
import Sidebar from "@/components/Sidebar";
|
||||
@ -272,7 +272,10 @@ export default function PacientesPage() {
|
||||
<td className="p-4">
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<div className="text-blue-600 cursor-pointer">Ações</div>
|
||||
<Button variant="ghost" className="h-8 w-8 p-0">
|
||||
<span className="sr-only">Abrir menu</span>
|
||||
<MoreVertical className="h-4 w-4" />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end">
|
||||
<DropdownMenuItem onClick={() => openDetailsDialog(String(patient.id))}>
|
||||
|
||||
@ -9,7 +9,7 @@ export const reportsApi = {
|
||||
return data;
|
||||
} catch (error) {
|
||||
console.error("Failed to fetch reports:", error);
|
||||
return [];
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
getReportById: async (reportId) => {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user