J.R.
This commit is contained in:
parent
b9b49cba42
commit
8a63219cf6
@ -106,10 +106,6 @@ export default function NovoLaudoPage() {
|
|||||||
<CardContent>
|
<CardContent>
|
||||||
<form onSubmit={handleSubmit} className="space-y-6">
|
<form onSubmit={handleSubmit} className="space-y-6">
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-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">
|
<div className="space-y-2">
|
||||||
<Label htmlFor="exam">Exame</Label>
|
<Label htmlFor="exam">Exame</Label>
|
||||||
<Input id="exam" value={formData.exam} onChange={handleInputChange} />
|
<Input id="exam" value={formData.exam} onChange={handleInputChange} />
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
import { useEffect, useState, useCallback } from "react";
|
import { useEffect, useState, useCallback } from "react";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@/components/ui/dropdown-menu";
|
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 { api } from "@/services/api.mjs";
|
||||||
import { PatientDetailsModal } from "@/components/ui/patient-details-modal";
|
import { PatientDetailsModal } from "@/components/ui/patient-details-modal";
|
||||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
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>
|
<SelectItem value="20">20 por página</SelectItem>
|
||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</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">
|
<Button variant="default" className="bg-green-600 hover:bg-green-700 w-full sm:w-auto">
|
||||||
Novo Paciente
|
Novo Paciente
|
||||||
</Button>
|
</Button>
|
||||||
@ -262,9 +262,10 @@ export default function PacientesPage() {
|
|||||||
<td className="p-3 sm:p-4">
|
<td className="p-3 sm:p-4">
|
||||||
<DropdownMenu>
|
<DropdownMenu>
|
||||||
<DropdownMenuTrigger asChild>
|
<DropdownMenuTrigger asChild>
|
||||||
<button className="text-primary hover:underline text-sm sm:text-base">
|
<Button variant="ghost" className="h-8 w-8 p-0">
|
||||||
Ações
|
<span className="sr-only">Abrir menu</span>
|
||||||
</button>
|
<MoreVertical className="h-4 w-4" />
|
||||||
|
</Button>
|
||||||
</DropdownMenuTrigger>
|
</DropdownMenuTrigger>
|
||||||
<DropdownMenuContent align="end">
|
<DropdownMenuContent align="end">
|
||||||
<DropdownMenuItem onClick={() => handleOpenModal(p)}>
|
<DropdownMenuItem onClick={() => handleOpenModal(p)}>
|
||||||
@ -272,16 +273,16 @@ export default function PacientesPage() {
|
|||||||
Ver detalhes
|
Ver detalhes
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
<DropdownMenuItem asChild>
|
<DropdownMenuItem asChild>
|
||||||
<Link href={`/doctor/pacientes/${p.id}/laudos`}>
|
<Link href={`/doctor/medicos/${p.id}/laudos`}>
|
||||||
<Edit className="w-4 h-4 mr-2" />
|
<Edit className="w-4 h-4 mr-2" />
|
||||||
Laudos
|
Laudos
|
||||||
</Link>
|
</Link>
|
||||||
</DropdownMenuItem>
|
</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" />
|
<Calendar className="w-4 h-4 mr-2" />
|
||||||
Ver agenda
|
Ver agenda
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem> */}
|
||||||
<DropdownMenuItem
|
{/* <DropdownMenuItem
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
const newPacientes = pacientes.filter((pac) => pac.id !== p.id);
|
const newPacientes = pacientes.filter((pac) => pac.id !== p.id);
|
||||||
setPacientes(newPacientes);
|
setPacientes(newPacientes);
|
||||||
@ -291,7 +292,7 @@ export default function PacientesPage() {
|
|||||||
>
|
>
|
||||||
<Trash2 className="w-4 h-4 mr-2" />
|
<Trash2 className="w-4 h-4 mr-2" />
|
||||||
Excluir
|
Excluir
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem> */}
|
||||||
</DropdownMenuContent>
|
</DropdownMenuContent>
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
</td>
|
</td>
|
||||||
@ -387,11 +388,10 @@ export default function PacientesPage() {
|
|||||||
<button
|
<button
|
||||||
key={number}
|
key={number}
|
||||||
onClick={() => paginate(number)}
|
onClick={() => paginate(number)}
|
||||||
className={`px-4 py-2 rounded-md font-medium transition-colors text-sm border border-border ${
|
className={`px-4 py-2 rounded-md font-medium transition-colors text-sm border border-border ${currentPage === number
|
||||||
currentPage === number
|
|
||||||
? "bg-green-600 text-primary-foreground shadow-md border-green-600"
|
? "bg-green-600 text-primary-foreground shadow-md border-green-600"
|
||||||
: "bg-secondary text-secondary-foreground hover:bg-secondary/80"
|
: "bg-secondary text-secondary-foreground hover:bg-secondary/80"
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{number}
|
{number}
|
||||||
</button>
|
</button>
|
||||||
@ -411,11 +411,11 @@ export default function PacientesPage() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<PatientDetailsModal
|
<PatientDetailsModal
|
||||||
patient={selectedPatient}
|
patient={selectedPatient}
|
||||||
isOpen={isModalOpen}
|
isOpen={isModalOpen}
|
||||||
onClose={handleCloseModal}
|
onClose={handleCloseModal}
|
||||||
/>
|
/>
|
||||||
</Sidebar>
|
</Sidebar>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -6,7 +6,7 @@ import { useRouter } from "next/navigation";
|
|||||||
import { Button } from "@/components/ui/button"
|
import { Button } from "@/components/ui/button"
|
||||||
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@/components/ui/dropdown-menu"
|
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@/components/ui/dropdown-menu"
|
||||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"
|
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 { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle } from "@/components/ui/alert-dialog"
|
||||||
|
|
||||||
import { doctorsService } from "services/doctorsApi.mjs";
|
import { doctorsService } from "services/doctorsApi.mjs";
|
||||||
@ -284,7 +284,10 @@ export default function DoctorsPage() {
|
|||||||
<td className="px-4 py-3 text-right">
|
<td className="px-4 py-3 text-right">
|
||||||
<DropdownMenu>
|
<DropdownMenu>
|
||||||
<DropdownMenuTrigger asChild>
|
<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>
|
</DropdownMenuTrigger>
|
||||||
<DropdownMenuContent align="end">
|
<DropdownMenuContent align="end">
|
||||||
<DropdownMenuItem onClick={() => openDetailsDialog(doctor)}>
|
<DropdownMenuItem onClick={() => openDetailsDialog(doctor)}>
|
||||||
@ -342,7 +345,7 @@ export default function DoctorsPage() {
|
|||||||
</div>
|
</div>
|
||||||
<DropdownMenu>
|
<DropdownMenu>
|
||||||
<DropdownMenuTrigger asChild>
|
<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>
|
</DropdownMenuTrigger>
|
||||||
<DropdownMenuContent align="end">
|
<DropdownMenuContent align="end">
|
||||||
<DropdownMenuItem onClick={() => openDetailsDialog(doctor)}>
|
<DropdownMenuItem onClick={() => openDetailsDialog(doctor)}>
|
||||||
@ -386,11 +389,10 @@ export default function DoctorsPage() {
|
|||||||
<button
|
<button
|
||||||
key={number}
|
key={number}
|
||||||
onClick={() => paginate(number)}
|
onClick={() => paginate(number)}
|
||||||
className={`px-4 py-2 rounded-md font-medium transition-colors text-sm border border-gray-300 ${
|
className={`px-4 py-2 rounded-md font-medium transition-colors text-sm border border-gray-300 ${currentPage === number
|
||||||
currentPage === number
|
|
||||||
? "bg-green-600 text-white shadow-md border-green-600"
|
? "bg-green-600 text-white shadow-md border-green-600"
|
||||||
: "bg-gray-100 text-gray-700 hover:bg-gray-200"
|
: "bg-gray-100 text-gray-700 hover:bg-gray-200"
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{number}
|
{number}
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@ -5,7 +5,7 @@ import Link from "next/link";
|
|||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@/components/ui/dropdown-menu";
|
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@/components/ui/dropdown-menu";
|
||||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
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 { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle } from "@/components/ui/alert-dialog";
|
||||||
import { patientsService } from "@/services/patientsApi.mjs";
|
import { patientsService } from "@/services/patientsApi.mjs";
|
||||||
import Sidebar from "@/components/Sidebar";
|
import Sidebar from "@/components/Sidebar";
|
||||||
@ -264,7 +264,10 @@ export default function PacientesPage() {
|
|||||||
<td className="p-4">
|
<td className="p-4">
|
||||||
<DropdownMenu>
|
<DropdownMenu>
|
||||||
<DropdownMenuTrigger asChild>
|
<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>
|
</DropdownMenuTrigger>
|
||||||
<DropdownMenuContent align="end">
|
<DropdownMenuContent align="end">
|
||||||
<DropdownMenuItem onClick={() => openDetailsDialog(String(patient.id))}>
|
<DropdownMenuItem onClick={() => openDetailsDialog(String(patient.id))}>
|
||||||
@ -321,38 +324,41 @@ export default function PacientesPage() {
|
|||||||
{patient.nome}
|
{patient.nome}
|
||||||
{patient.vip && (
|
{patient.vip && (
|
||||||
<span className="ml-2 px-2 py-0.5 text-xs font-semibold text-purple-600 bg-purple-100 rounded-full">VIP</span>
|
<span className="ml-2 px-2 py-0.5 text-xs font-semibold text-purple-600 bg-purple-100 rounded-full">VIP</span>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="text-sm text-gray-600">Telefone: {patient.telefone}</div>
|
<div className="text-sm text-gray-600">Telefone: {patient.telefone}</div>
|
||||||
<div className="text-sm text-gray-600">Convênio: {patient.convenio}</div>
|
<div className="text-sm text-gray-600">Convênio: {patient.convenio}</div>
|
||||||
</div>
|
</div>
|
||||||
<DropdownMenu>
|
<DropdownMenu>
|
||||||
<DropdownMenuTrigger asChild>
|
<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">
|
||||||
</DropdownMenuTrigger>
|
<span className="sr-only">Abrir menu</span>
|
||||||
<DropdownMenuContent align="end">
|
<MoreVertical className="h-4 w-4" />
|
||||||
<DropdownMenuItem onClick={() => openDetailsDialog(String(patient.id))}>
|
</Button>
|
||||||
<Eye className="w-4 h-4 mr-2" />
|
</DropdownMenuTrigger>
|
||||||
Ver detalhes
|
<DropdownMenuContent align="end">
|
||||||
</DropdownMenuItem>
|
<DropdownMenuItem onClick={() => openDetailsDialog(String(patient.id))}>
|
||||||
|
<Eye className="w-4 h-4 mr-2" />
|
||||||
|
Ver detalhes
|
||||||
|
</DropdownMenuItem>
|
||||||
|
|
||||||
<DropdownMenuItem asChild>
|
<DropdownMenuItem asChild>
|
||||||
<Link href={`/secretary/pacientes/${patient.id}/editar`} className="flex items-center w-full">
|
<Link href={`/secretary/pacientes/${patient.id}/editar`} className="flex items-center w-full">
|
||||||
<Edit className="w-4 h-4 mr-2" />
|
<Edit className="w-4 h-4 mr-2" />
|
||||||
Editar
|
Editar
|
||||||
</Link>
|
</Link>
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
|
|
||||||
<DropdownMenuItem>
|
<DropdownMenuItem>
|
||||||
<Calendar className="w-4 h-4 mr-2" />
|
<Calendar className="w-4 h-4 mr-2" />
|
||||||
Marcar consulta
|
Marcar consulta
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
<DropdownMenuItem className="text-red-600" onClick={() => openDeleteDialog(String(patient.id))}>
|
<DropdownMenuItem className="text-red-600" onClick={() => openDeleteDialog(String(patient.id))}>
|
||||||
<Trash2 className="w-4 h-4 mr-2" />
|
<Trash2 className="w-4 h-4 mr-2" />
|
||||||
Excluir
|
Excluir
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
</DropdownMenuContent>
|
</DropdownMenuContent>
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
@ -430,65 +436,65 @@ export default function PacientesPage() {
|
|||||||
<div className="grid gap-4 py-4">
|
<div className="grid gap-4 py-4">
|
||||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||||
<div>
|
<div>
|
||||||
<p className="font-semibold">Nome Completo</p>
|
<p className="font-semibold">Nome Completo</p>
|
||||||
<p>{patientDetails.full_name}</p>
|
<p>{patientDetails.full_name}</p>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<p className="font-semibold">Email</p>
|
<p className="font-semibold">Email</p>
|
||||||
<p>{patientDetails.email}</p>
|
<p>{patientDetails.email}</p>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<p className="font-semibold">Telefone</p>
|
<p className="font-semibold">Telefone</p>
|
||||||
<p>{patientDetails.phone_mobile}</p>
|
<p>{patientDetails.phone_mobile}</p>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<p className="font-semibold">Data de Nascimento</p>
|
<p className="font-semibold">Data de Nascimento</p>
|
||||||
<p>{patientDetails.birth_date}</p>
|
<p>{patientDetails.birth_date}</p>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<p className="font-semibold">CPF</p>
|
<p className="font-semibold">CPF</p>
|
||||||
<p>{patientDetails.cpf}</p>
|
<p>{patientDetails.cpf}</p>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<p className="font-semibold">Tipo Sanguíneo</p>
|
<p className="font-semibold">Tipo Sanguíneo</p>
|
||||||
<p>{patientDetails.blood_type}</p>
|
<p>{patientDetails.blood_type}</p>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<p className="font-semibold">Peso (kg)</p>
|
<p className="font-semibold">Peso (kg)</p>
|
||||||
<p>{patientDetails.weight_kg}</p>
|
<p>{patientDetails.weight_kg}</p>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<p className="font-semibold">Altura (m)</p>
|
<p className="font-semibold">Altura (m)</p>
|
||||||
<p>{patientDetails.height_m}</p>
|
<p>{patientDetails.height_m}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="border-t pt-4 mt-4">
|
<div className="border-t pt-4 mt-4">
|
||||||
<h3 className="font-semibold mb-2">Endereço</h3>
|
<h3 className="font-semibold mb-2">Endereço</h3>
|
||||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||||
<div>
|
<div>
|
||||||
<p className="font-semibold">Rua</p>
|
<p className="font-semibold">Rua</p>
|
||||||
<p>{`${patientDetails.street}, ${patientDetails.number}`}</p>
|
<p>{`${patientDetails.street}, ${patientDetails.number}`}</p>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<p className="font-semibold">Complemento</p>
|
<p className="font-semibold">Complemento</p>
|
||||||
<p>{patientDetails.complement}</p>
|
<p>{patientDetails.complement}</p>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<p className="font-semibold">Bairro</p>
|
<p className="font-semibold">Bairro</p>
|
||||||
<p>{patientDetails.neighborhood}</p>
|
<p>{patientDetails.neighborhood}</p>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<p className="font-semibold">Cidade</p>
|
<p className="font-semibold">Cidade</p>
|
||||||
<p>{patientDetails.cidade}</p>
|
<p>{patientDetails.cidade}</p>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<p className="font-semibold">Estado</p>
|
<p className="font-semibold">Estado</p>
|
||||||
<p>{patientDetails.estado}</p>
|
<p>{patientDetails.estado}</p>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<p className="font-semibold">CEP</p>
|
<p className="font-semibold">CEP</p>
|
||||||
<p>{patientDetails.cep}</p>
|
<p>{patientDetails.cep}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,159 +1,67 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import { useState, useEffect } from "react"
|
import { useState, useEffect, useMemo } from "react"
|
||||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
|
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
|
||||||
import { Badge } from "@/components/ui/badge"
|
import { Badge } from "@/components/ui/badge"
|
||||||
import { Button } from "@/components/ui/button"
|
import { Button } from "@/components/ui/button"
|
||||||
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from "@/components/ui/dialog"
|
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from "@/components/ui/dialog"
|
||||||
import { toast } from "@/hooks/use-toast"
|
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 Sidebar from "@/components/Sidebar"
|
||||||
|
import { useAuthLayout } from "@/hooks/useAuthLayout"
|
||||||
|
import { reportsApi } from "@/services/reportsApi.mjs"
|
||||||
|
|
||||||
interface Report {
|
interface Report {
|
||||||
id: string
|
id: string;
|
||||||
title: string
|
order_number: string;
|
||||||
doctor: string
|
patient_id: string;
|
||||||
date: string
|
status: string;
|
||||||
type: string
|
exam: string;
|
||||||
status: "disponivel" | "pendente"
|
requested_by: string;
|
||||||
description: string
|
cid_code: string;
|
||||||
content: {
|
diagnosis: string;
|
||||||
patientInfo: {
|
conclusion: string;
|
||||||
name: string
|
content_html: string;
|
||||||
age: number
|
content_json: any;
|
||||||
gender: string
|
hide_date: boolean;
|
||||||
id: string
|
hide_signature: boolean;
|
||||||
}
|
due_at: string;
|
||||||
examDetails: {
|
created_by: string;
|
||||||
requestingDoctor: string
|
updated_by: string;
|
||||||
examDate: string
|
created_at: string;
|
||||||
reportDate: string
|
updated_at: string;
|
||||||
technique: string
|
|
||||||
}
|
|
||||||
findings: string
|
|
||||||
conclusion: string
|
|
||||||
recommendations?: string
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function ReportsPage() {
|
export default function ReportsPage() {
|
||||||
const [reports, setReports] = useState<Report[]>([])
|
const [reports, setReports] = useState<Report[]>([])
|
||||||
const [selectedReport, setSelectedReport] = useState<Report | null>(null)
|
const [selectedReport, setSelectedReport] = useState<Report | null>(null)
|
||||||
const [isViewModalOpen, setIsViewModalOpen] = useState(false)
|
const [isViewModalOpen, setIsViewModalOpen] = useState(false)
|
||||||
|
const [isLoading, setIsLoading] = useState(true)
|
||||||
|
const requiredRole = useMemo(() => ["paciente"], []);
|
||||||
|
const { user, isLoading: isAuthLoading } = useAuthLayout({ requiredRole });
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const mockReports: Report[] = [
|
if (user) {
|
||||||
{
|
const fetchReports = async () => {
|
||||||
id: "1",
|
try {
|
||||||
title: "Exame de Sangue - Hemograma Completo",
|
setIsLoading(true);
|
||||||
doctor: "Dr. João Silva",
|
const fetchedReports = await reportsApi.getReports(user.id);
|
||||||
date: "2024-01-15",
|
setReports(fetchedReports);
|
||||||
type: "Exame Laboratorial",
|
} catch (error) {
|
||||||
status: "disponivel",
|
console.error("Erro ao buscar laudos:", error)
|
||||||
description: "Hemograma completo com contagem de células sanguíneas",
|
toast({
|
||||||
content: {
|
title: "Erro ao buscar laudos",
|
||||||
patientInfo: {
|
description: "Não foi possível carregar os laudos. Tente novamente.",
|
||||||
name: "Maria Silva Santos",
|
variant: "destructive",
|
||||||
age: 35,
|
})
|
||||||
gender: "Feminino",
|
} finally {
|
||||||
id: "123.456.789-00",
|
setIsLoading(false);
|
||||||
},
|
}
|
||||||
examDetails: {
|
}
|
||||||
requestingDoctor: "Dr. João Silva - CRM 12345",
|
fetchReports()
|
||||||
examDate: "15/01/2024",
|
}
|
||||||
reportDate: "15/01/2024",
|
}, [user?.id])
|
||||||
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)
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
const handleViewReport = (reportId: string) => {
|
const handleViewReport = (reportId: string) => {
|
||||||
const report = reports.find((r) => r.id === reportId)
|
const report = reports.find((r) => r.id === reportId)
|
||||||
@ -168,100 +76,23 @@ export default function ReportsPage() {
|
|||||||
if (!report) return
|
if (!report) return
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Simular loading
|
|
||||||
toast({
|
toast({
|
||||||
title: "Preparando download...",
|
title: "Preparando download...",
|
||||||
description: "Gerando PDF do laudo médico",
|
description: "Gerando PDF do laudo médico",
|
||||||
})
|
})
|
||||||
|
|
||||||
// Criar conteúdo HTML do laudo para conversão em PDF
|
const htmlContent = report.content_html;
|
||||||
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>
|
|
||||||
|
|
||||||
<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 blob = new Blob([htmlContent], { type: "text/html" })
|
||||||
const url = URL.createObjectURL(blob)
|
const url = URL.createObjectURL(blob)
|
||||||
|
|
||||||
// Criar link temporário para download
|
|
||||||
const link = document.createElement("a")
|
const link = document.createElement("a")
|
||||||
link.href = url
|
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)
|
document.body.appendChild(link)
|
||||||
link.click()
|
link.click()
|
||||||
document.body.removeChild(link)
|
document.body.removeChild(link)
|
||||||
|
|
||||||
// Limpar URL temporária
|
|
||||||
URL.revokeObjectURL(url)
|
URL.revokeObjectURL(url)
|
||||||
|
|
||||||
toast({
|
toast({
|
||||||
@ -283,8 +114,18 @@ export default function ReportsPage() {
|
|||||||
setSelectedReport(null)
|
setSelectedReport(null)
|
||||||
}
|
}
|
||||||
|
|
||||||
const availableReports = reports.filter((report) => report.status === "disponivel")
|
const availableReports = reports.filter((report) => report.status.toLowerCase() === "draft")
|
||||||
const pendingReports = reports.filter((report) => report.status === "pendente")
|
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 (
|
return (
|
||||||
<Sidebar>
|
<Sidebar>
|
||||||
@ -333,25 +174,25 @@ export default function ReportsPage() {
|
|||||||
<CardHeader>
|
<CardHeader>
|
||||||
<div className="flex items-start justify-between">
|
<div className="flex items-start justify-between">
|
||||||
<div className="space-y-1">
|
<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">
|
<CardDescription className="flex items-center gap-4">
|
||||||
<span className="flex items-center gap-1">
|
<span className="flex items-center gap-1">
|
||||||
<User className="h-4 w-4" />
|
<User className="h-4 w-4" />
|
||||||
{report.doctor}
|
{report.requested_by}
|
||||||
</span>
|
</span>
|
||||||
<span className="flex items-center gap-1">
|
<span className="flex items-center gap-1">
|
||||||
<Calendar className="h-4 w-4" />
|
<Calendar className="h-4 w-4" />
|
||||||
{new Date(report.date).toLocaleDateString("pt-BR")}
|
{new Date(report.created_at).toLocaleDateString("pt-BR")}
|
||||||
</span>
|
</span>
|
||||||
</CardDescription>
|
</CardDescription>
|
||||||
</div>
|
</div>
|
||||||
<Badge variant="secondary" className="bg-green-100 text-green-800">
|
<Badge variant="secondary" className="bg-green-100 text-green-800">
|
||||||
{report.type}
|
Finalizado
|
||||||
</Badge>
|
</Badge>
|
||||||
</div>
|
</div>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent>
|
<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">
|
<div className="flex gap-2">
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
@ -369,7 +210,7 @@ export default function ReportsPage() {
|
|||||||
className="flex items-center gap-2"
|
className="flex items-center gap-2"
|
||||||
>
|
>
|
||||||
<Download className="h-4 w-4" />
|
<Download className="h-4 w-4" />
|
||||||
Baixar PDF
|
Baixar
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
@ -388,25 +229,25 @@ export default function ReportsPage() {
|
|||||||
<CardHeader>
|
<CardHeader>
|
||||||
<div className="flex items-start justify-between">
|
<div className="flex items-start justify-between">
|
||||||
<div className="space-y-1">
|
<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">
|
<CardDescription className="flex items-center gap-4">
|
||||||
<span className="flex items-center gap-1">
|
<span className="flex items-center gap-1">
|
||||||
<User className="h-4 w-4" />
|
<User className="h-4 w-4" />
|
||||||
{report.doctor}
|
{report.requested_by}
|
||||||
</span>
|
</span>
|
||||||
<span className="flex items-center gap-1">
|
<span className="flex items-center gap-1">
|
||||||
<Calendar className="h-4 w-4" />
|
<Calendar className="h-4 w-4" />
|
||||||
{new Date(report.date).toLocaleDateString("pt-BR")}
|
{new Date(report.created_at).toLocaleDateString("pt-BR")}
|
||||||
</span>
|
</span>
|
||||||
</CardDescription>
|
</CardDescription>
|
||||||
</div>
|
</div>
|
||||||
<Badge variant="secondary" className="bg-yellow-100 text-yellow-800">
|
<Badge variant="secondary" className="bg-yellow-100 text-yellow-800">
|
||||||
Pendente
|
{report.status}
|
||||||
</Badge>
|
</Badge>
|
||||||
</div>
|
</div>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent>
|
<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">
|
<p className="text-sm text-yellow-600 font-medium">
|
||||||
Laudo em processamento. Você será notificado quando estiver disponível.
|
Laudo em processamento. Você será notificado quando estiver disponível.
|
||||||
</p>
|
</p>
|
||||||
@ -417,7 +258,7 @@ export default function ReportsPage() {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{reports.length === 0 && (
|
{reports.length === 0 && !isLoading && (
|
||||||
<Card className="text-center py-12">
|
<Card className="text-center py-12">
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<FileText className="h-12 w-12 text-gray-400 mx-auto mb-4" />
|
<FileText className="h-12 w-12 text-gray-400 mx-auto mb-4" />
|
||||||
@ -432,9 +273,9 @@ export default function ReportsPage() {
|
|||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<div>
|
<div>
|
||||||
<DialogTitle className="text-xl font-bold">{selectedReport?.title}</DialogTitle>
|
<DialogTitle className="text-xl font-bold">{selectedReport?.exam}</DialogTitle>
|
||||||
<DialogDescription className="mt-1">
|
<DialogDescription className="mt-1">
|
||||||
{selectedReport?.type} - {selectedReport?.doctor}
|
{selectedReport?.order_number}
|
||||||
</DialogDescription>
|
</DialogDescription>
|
||||||
</div>
|
</div>
|
||||||
<Button variant="ghost" size="sm" onClick={handleCloseModal} className="h-8 w-8 p-0">
|
<Button variant="ghost" size="sm" onClick={handleCloseModal} className="h-8 w-8 p-0">
|
||||||
@ -444,94 +285,7 @@ export default function ReportsPage() {
|
|||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
|
|
||||||
{selectedReport && (
|
{selectedReport && (
|
||||||
<div className="space-y-6 mt-4">
|
<div className="space-y-6 mt-4" dangerouslySetInnerHTML={{ __html: selectedReport.content_html }} />
|
||||||
<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>
|
|
||||||
)}
|
)}
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
|||||||
@ -6,7 +6,7 @@ import Link from "next/link";
|
|||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@/components/ui/dropdown-menu";
|
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@/components/ui/dropdown-menu";
|
||||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
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 { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle } from "@/components/ui/alert-dialog";
|
||||||
import { patientsService } from "@/services/patientsApi.mjs";
|
import { patientsService } from "@/services/patientsApi.mjs";
|
||||||
import Sidebar from "@/components/Sidebar";
|
import Sidebar from "@/components/Sidebar";
|
||||||
@ -164,7 +164,7 @@ export default function PacientesPage() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Bloco de Filtros (Responsividade APLICADA) */}
|
{/* Bloco de Filtros (Responsividade APLICADA) */}
|
||||||
<div className="flex flex-wrap items-center gap-4 bg-card p-4 rounded-lg border border-border">
|
<div className="flex flex-wrap items-center gap-4 bg-card p-4 rounded-lg border border-border">
|
||||||
<Filter className="w-5 h-5 text-gray-400" />
|
<Filter className="w-5 h-5 text-gray-400" />
|
||||||
|
|
||||||
{/* Busca - Ocupa 100% no mobile, depois cresce */}
|
{/* Busca - Ocupa 100% no mobile, depois cresce */}
|
||||||
@ -272,7 +272,10 @@ export default function PacientesPage() {
|
|||||||
<td className="p-4">
|
<td className="p-4">
|
||||||
<DropdownMenu>
|
<DropdownMenu>
|
||||||
<DropdownMenuTrigger asChild>
|
<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>
|
</DropdownMenuTrigger>
|
||||||
<DropdownMenuContent align="end">
|
<DropdownMenuContent align="end">
|
||||||
<DropdownMenuItem onClick={() => openDetailsDialog(String(patient.id))}>
|
<DropdownMenuItem onClick={() => openDetailsDialog(String(patient.id))}>
|
||||||
@ -329,38 +332,38 @@ export default function PacientesPage() {
|
|||||||
{patient.nome}
|
{patient.nome}
|
||||||
{patient.vip && (
|
{patient.vip && (
|
||||||
<span className="ml-2 px-2 py-0.5 text-xs font-semibold text-purple-600 bg-purple-100 rounded-full">VIP</span>
|
<span className="ml-2 px-2 py-0.5 text-xs font-semibold text-purple-600 bg-purple-100 rounded-full">VIP</span>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="text-sm text-gray-600">Telefone: {patient.telefone}</div>
|
<div className="text-sm text-gray-600">Telefone: {patient.telefone}</div>
|
||||||
<div className="text-sm text-gray-600">Convênio: {patient.convenio}</div>
|
<div className="text-sm text-gray-600">Convênio: {patient.convenio}</div>
|
||||||
</div>
|
</div>
|
||||||
<DropdownMenu>
|
<DropdownMenu>
|
||||||
<DropdownMenuTrigger asChild>
|
<DropdownMenuTrigger asChild>
|
||||||
<div className="w-full"><Button variant="outline" className="w-full">Ações</Button></div>
|
<div className="w-full"><Button variant="outline" className="w-full">Ações</Button></div>
|
||||||
</DropdownMenuTrigger>
|
</DropdownMenuTrigger>
|
||||||
<DropdownMenuContent align="end">
|
<DropdownMenuContent align="end">
|
||||||
<DropdownMenuItem onClick={() => openDetailsDialog(String(patient.id))}>
|
<DropdownMenuItem onClick={() => openDetailsDialog(String(patient.id))}>
|
||||||
<Eye className="w-4 h-4 mr-2" />
|
<Eye className="w-4 h-4 mr-2" />
|
||||||
Ver detalhes
|
Ver detalhes
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
|
|
||||||
<DropdownMenuItem asChild>
|
<DropdownMenuItem asChild>
|
||||||
<Link href={`/secretary/pacientes/${patient.id}/editar`} className="flex items-center w-full">
|
<Link href={`/secretary/pacientes/${patient.id}/editar`} className="flex items-center w-full">
|
||||||
<Edit className="w-4 h-4 mr-2" />
|
<Edit className="w-4 h-4 mr-2" />
|
||||||
Editar
|
Editar
|
||||||
</Link>
|
</Link>
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
|
|
||||||
<DropdownMenuItem>
|
<DropdownMenuItem>
|
||||||
<Calendar className="w-4 h-4 mr-2" />
|
<Calendar className="w-4 h-4 mr-2" />
|
||||||
Marcar consulta
|
Marcar consulta
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
<DropdownMenuItem className="text-red-600" onClick={() => openDeleteDialog(String(patient.id))}>
|
<DropdownMenuItem className="text-red-600" onClick={() => openDeleteDialog(String(patient.id))}>
|
||||||
<Trash2 className="w-4 h-4 mr-2" />
|
<Trash2 className="w-4 h-4 mr-2" />
|
||||||
Excluir
|
Excluir
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
</DropdownMenuContent>
|
</DropdownMenuContent>
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
@ -438,65 +441,65 @@ export default function PacientesPage() {
|
|||||||
<div className="grid gap-4 py-4">
|
<div className="grid gap-4 py-4">
|
||||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||||
<div>
|
<div>
|
||||||
<p className="font-semibold">Nome Completo</p>
|
<p className="font-semibold">Nome Completo</p>
|
||||||
<p>{patientDetails.full_name}</p>
|
<p>{patientDetails.full_name}</p>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<p className="font-semibold">Email</p>
|
<p className="font-semibold">Email</p>
|
||||||
<p>{patientDetails.email}</p>
|
<p>{patientDetails.email}</p>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<p className="font-semibold">Telefone</p>
|
<p className="font-semibold">Telefone</p>
|
||||||
<p>{patientDetails.phone_mobile}</p>
|
<p>{patientDetails.phone_mobile}</p>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<p className="font-semibold">Data de Nascimento</p>
|
<p className="font-semibold">Data de Nascimento</p>
|
||||||
<p>{patientDetails.birth_date}</p>
|
<p>{patientDetails.birth_date}</p>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<p className="font-semibold">CPF</p>
|
<p className="font-semibold">CPF</p>
|
||||||
<p>{patientDetails.cpf}</p>
|
<p>{patientDetails.cpf}</p>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<p className="font-semibold">Tipo Sanguíneo</p>
|
<p className="font-semibold">Tipo Sanguíneo</p>
|
||||||
<p>{patientDetails.blood_type}</p>
|
<p>{patientDetails.blood_type}</p>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<p className="font-semibold">Peso (kg)</p>
|
<p className="font-semibold">Peso (kg)</p>
|
||||||
<p>{patientDetails.weight_kg}</p>
|
<p>{patientDetails.weight_kg}</p>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<p className="font-semibold">Altura (m)</p>
|
<p className="font-semibold">Altura (m)</p>
|
||||||
<p>{patientDetails.height_m}</p>
|
<p>{patientDetails.height_m}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="border-t pt-4 mt-4">
|
<div className="border-t pt-4 mt-4">
|
||||||
<h3 className="font-semibold mb-2">Endereço</h3>
|
<h3 className="font-semibold mb-2">Endereço</h3>
|
||||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||||
<div>
|
<div>
|
||||||
<p className="font-semibold">Rua</p>
|
<p className="font-semibold">Rua</p>
|
||||||
<p>{`${patientDetails.street}, ${patientDetails.number}`}</p>
|
<p>{`${patientDetails.street}, ${patientDetails.number}`}</p>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<p className="font-semibold">Complemento</p>
|
<p className="font-semibold">Complemento</p>
|
||||||
<p>{patientDetails.complement}</p>
|
<p>{patientDetails.complement}</p>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<p className="font-semibold">Bairro</p>
|
<p className="font-semibold">Bairro</p>
|
||||||
<p>{patientDetails.neighborhood}</p>
|
<p>{patientDetails.neighborhood}</p>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<p className="font-semibold">Cidade</p>
|
<p className="font-semibold">Cidade</p>
|
||||||
<p>{patientDetails.cidade}</p>
|
<p>{patientDetails.cidade}</p>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<p className="font-semibold">Estado</p>
|
<p className="font-semibold">Estado</p>
|
||||||
<p>{patientDetails.estado}</p>
|
<p>{patientDetails.estado}</p>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<p className="font-semibold">CEP</p>
|
<p className="font-semibold">CEP</p>
|
||||||
<p>{patientDetails.cep}</p>
|
<p>{patientDetails.cep}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -9,7 +9,7 @@ export const reportsApi = {
|
|||||||
return data;
|
return data;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Failed to fetch reports:", error);
|
console.error("Failed to fetch reports:", error);
|
||||||
return [];
|
throw error;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
getReportById: async (reportId) => {
|
getReportById: async (reportId) => {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user