pequenos ajustes
This commit is contained in:
parent
93ea8709d6
commit
74a7fa91de
@ -144,10 +144,6 @@ export default function EditarLaudoPage() {
|
||||
<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} />
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
|
||||
"use client";
|
||||
"use client";
|
||||
|
||||
import { useParams, useRouter } from "next/navigation";
|
||||
import { useState } from "react";
|
||||
@ -106,10 +105,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} />
|
||||
|
||||
@ -10,7 +10,7 @@ import {
|
||||
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 {
|
||||
@ -272,9 +272,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)}>
|
||||
@ -282,15 +283,11 @@ 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}`)}>
|
||||
<Calendar className="w-4 h-4 mr-2" />
|
||||
Ver agenda
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem
|
||||
onClick={() => {
|
||||
// Simulação de exclusão (A exclusão real deve ser feita via API)
|
||||
|
||||
@ -7,7 +7,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 { 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,
|
||||
@ -308,7 +308,9 @@ export default function DoctorsPage() {
|
||||
{/* ===== INÍCIO DA ALTERAÇÃO ===== */}
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<div className="text-blue-600 cursor-pointer inline-block">Ações</div>
|
||||
<div className="text-black-600 cursor-pointer inline-block">
|
||||
<MoreVertical className="h-4 w-4" />
|
||||
</div>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end">
|
||||
<DropdownMenuItem onClick={() => openDetailsDialog(doctor)}>
|
||||
|
||||
@ -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 ManagerLayout from "@/components/manager-layout";
|
||||
@ -267,7 +267,9 @@ export default function PacientesPage() {
|
||||
<td className="p-4">
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<div className="text-blue-600 cursor-pointer">Ações</div>
|
||||
<div className="text-black-600 cursor-pointer">
|
||||
<MoreVertical className="h-4 w-4" />
|
||||
</div>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end">
|
||||
<DropdownMenuItem onClick={() => openDetailsDialog(String(patient.id))}>
|
||||
|
||||
@ -204,7 +204,7 @@ export default function SecretaryAppointments() {
|
||||
</Button>
|
||||
<Button variant="outline" size="sm" className="text-red-600 hover:text-red-700 hover:bg-red-50 bg-transparent" onClick={() => handleDelete(appointment)}>
|
||||
<Trash2 className="mr-2 h-4 w-4" />
|
||||
Deletar
|
||||
Cancelar
|
||||
</Button>
|
||||
</div>
|
||||
</CardContent>
|
||||
|
||||
@ -6,44 +6,44 @@ 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 SecretaryLayout from "@/components/secretary-layout";
|
||||
import { patientsService } from "@/services/patientsApi.mjs";
|
||||
|
||||
// Defina o tamanho da página.
|
||||
const PAGE_SIZE = 5;
|
||||
const PAGE_SIZE = 5;
|
||||
|
||||
export default function PacientesPage() {
|
||||
// --- ESTADOS DE DADOS E GERAL ---
|
||||
const [searchTerm, setSearchTerm] = useState("");
|
||||
const [convenioFilter, setConvenioFilter] = useState("all");
|
||||
const [vipFilter, setVipFilter] = useState("all");
|
||||
|
||||
// Lista completa, carregada da API uma única vez
|
||||
const [allPatients, setAllPatients] = useState<any[]>([]);
|
||||
// Lista após a aplicação dos filtros (base para a paginação)
|
||||
const [filteredPatients, setFilteredPatients] = useState<any[]>([]);
|
||||
|
||||
const [loading, setLoading] = useState(true);
|
||||
// Lista completa, carregada da API uma única vez
|
||||
const [allPatients, setAllPatients] = useState<any[]>([]);
|
||||
// Lista após a aplicação dos filtros (base para a paginação)
|
||||
const [filteredPatients, setFilteredPatients] = useState<any[]>([]);
|
||||
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
|
||||
// --- ESTADOS DE PAGINAÇÃO ---
|
||||
const [page, setPage] = useState(1);
|
||||
|
||||
|
||||
// CÁLCULO DA PAGINAÇÃO
|
||||
const totalPages = Math.ceil(filteredPatients.length / PAGE_SIZE);
|
||||
const startIndex = (page - 1) * PAGE_SIZE;
|
||||
const endIndex = startIndex + PAGE_SIZE;
|
||||
// Pacientes a serem exibidos na tabela (aplicando a paginação)
|
||||
const currentPatients = filteredPatients.slice(startIndex, endIndex);
|
||||
const currentPatients = filteredPatients.slice(startIndex, endIndex);
|
||||
|
||||
// --- ESTADOS DE DIALOGS ---
|
||||
const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);
|
||||
const [patientToDelete, setPatientToDelete] = useState<string | null>(null);
|
||||
const [detailsDialogOpen, setDetailsDialogOpen] = useState(false);
|
||||
const [patientDetails, setPatientDetails] = useState<any | null>(null);
|
||||
|
||||
|
||||
// --- FUNÇÕES DE LÓGICA ---
|
||||
|
||||
// 1. Função para carregar TODOS os pacientes da API
|
||||
@ -54,7 +54,7 @@ export default function PacientesPage() {
|
||||
try {
|
||||
// Como o backend retorna um array, chamamos sem paginação
|
||||
const res = await patientsService.list();
|
||||
|
||||
|
||||
const mapped = res.map((p: any) => ({
|
||||
id: String(p.id ?? ""),
|
||||
nome: p.full_name ?? "—",
|
||||
@ -62,7 +62,7 @@ export default function PacientesPage() {
|
||||
cidade: p.city ?? "—",
|
||||
estado: p.state ?? "—",
|
||||
// Formate as datas se necessário, aqui usamos como string
|
||||
ultimoAtendimento: p.last_visit_at?.split('T')[0] ?? "—",
|
||||
ultimoAtendimento: p.last_visit_at?.split('T')[0] ?? "—",
|
||||
proximoAtendimento: p.next_appointment_at?.split('T')[0] ?? "—",
|
||||
vip: Boolean(p.vip ?? false),
|
||||
convenio: p.convenio ?? "Particular", // Define um valor padrão
|
||||
@ -84,27 +84,27 @@ export default function PacientesPage() {
|
||||
useEffect(() => {
|
||||
const filtered = allPatients.filter((patient) => {
|
||||
// Filtro por termo de busca (Nome ou Telefone)
|
||||
const matchesSearch =
|
||||
patient.nome?.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||
const matchesSearch =
|
||||
patient.nome?.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||
patient.telefone?.includes(searchTerm);
|
||||
|
||||
|
||||
// Filtro por Convênio
|
||||
const matchesConvenio =
|
||||
convenioFilter === "all" ||
|
||||
const matchesConvenio =
|
||||
convenioFilter === "all" ||
|
||||
patient.convenio === convenioFilter;
|
||||
|
||||
|
||||
// Filtro por VIP
|
||||
const matchesVip =
|
||||
vipFilter === "all" ||
|
||||
(vipFilter === "vip" && patient.vip) ||
|
||||
const matchesVip =
|
||||
vipFilter === "all" ||
|
||||
(vipFilter === "vip" && patient.vip) ||
|
||||
(vipFilter === "regular" && !patient.vip);
|
||||
|
||||
return matchesSearch && matchesConvenio && matchesVip;
|
||||
});
|
||||
|
||||
|
||||
setFilteredPatients(filtered);
|
||||
// Garante que a página atual seja válida após a filtragem
|
||||
setPage(1);
|
||||
setPage(1);
|
||||
}, [allPatients, searchTerm, convenioFilter, vipFilter]);
|
||||
|
||||
// 3. Efeito inicial para buscar os pacientes
|
||||
@ -115,7 +115,7 @@ export default function PacientesPage() {
|
||||
|
||||
|
||||
// --- LÓGICA DE AÇÕES (DELETAR / VER DETALHES) ---
|
||||
|
||||
|
||||
const openDetailsDialog = async (patientId: string) => {
|
||||
setDetailsDialogOpen(true);
|
||||
setPatientDetails(null);
|
||||
@ -166,7 +166,7 @@ export default function PacientesPage() {
|
||||
{/* Bloco de Filtros (Responsividade APLICADA) */}
|
||||
<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" />
|
||||
|
||||
|
||||
{/* Busca - Ocupa 100% no mobile, depois cresce */}
|
||||
<input
|
||||
type="text"
|
||||
@ -207,7 +207,7 @@ export default function PacientesPage() {
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
|
||||
{/* Aniversariantes - Vai para a linha de baixo no mobile, ocupando 100% */}
|
||||
<Button variant="outline" className="w-full md:w-auto md:ml-auto">
|
||||
<Calendar className="w-4 h-4 mr-2" />
|
||||
@ -226,7 +226,7 @@ export default function PacientesPage() {
|
||||
</div>
|
||||
) : (
|
||||
// min-w ajustado para responsividade
|
||||
<table className="w-full min-w-[650px] md:min-w-[900px]">
|
||||
<table className="w-full min-w-[650px] md:min-w-[900px]">
|
||||
<thead className="bg-gray-50 border-b border-gray-200">
|
||||
<tr>
|
||||
<th className="text-left p-4 font-medium text-gray-700 w-[20%]">Nome</th>
|
||||
@ -271,11 +271,14 @@ export default function PacientesPage() {
|
||||
<td className="p-4 text-gray-600 hidden sm:table-cell">{patient.convenio}</td>
|
||||
<td className="p-4 text-gray-600 hidden lg:table-cell">{patient.ultimoAtendimento}</td>
|
||||
<td className="p-4 text-gray-600 hidden lg:table-cell">{patient.proximoAtendimento}</td>
|
||||
|
||||
|
||||
<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))}>
|
||||
@ -351,7 +354,7 @@ export default function PacientesPage() {
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
|
||||
{/* AlertDialogs (Permanecem os mesmos) */}
|
||||
<AlertDialog open={deleteDialogOpen} onOpenChange={setDeleteDialogOpen}>
|
||||
{/* ... (AlertDialog de Exclusão) ... */}
|
||||
@ -386,65 +389,65 @@ export default function PacientesPage() {
|
||||
<div className="grid gap-4 py-4">
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div>
|
||||
<p className="font-semibold">Nome Completo</p>
|
||||
<p>{patientDetails.full_name}</p>
|
||||
<p className="font-semibold">Nome Completo</p>
|
||||
<p>{patientDetails.full_name}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="font-semibold">Email</p>
|
||||
<p>{patientDetails.email}</p>
|
||||
<p className="font-semibold">Email</p>
|
||||
<p>{patientDetails.email}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="font-semibold">Telefone</p>
|
||||
<p>{patientDetails.phone_mobile}</p>
|
||||
<p className="font-semibold">Telefone</p>
|
||||
<p>{patientDetails.phone_mobile}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="font-semibold">Data de Nascimento</p>
|
||||
<p>{patientDetails.birth_date}</p>
|
||||
<p className="font-semibold">Data de Nascimento</p>
|
||||
<p>{patientDetails.birth_date}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="font-semibold">CPF</p>
|
||||
<p>{patientDetails.cpf}</p>
|
||||
<p className="font-semibold">CPF</p>
|
||||
<p>{patientDetails.cpf}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="font-semibold">Tipo Sanguíneo</p>
|
||||
<p>{patientDetails.blood_type}</p>
|
||||
<p className="font-semibold">Tipo Sanguíneo</p>
|
||||
<p>{patientDetails.blood_type}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="font-semibold">Peso (kg)</p>
|
||||
<p>{patientDetails.weight_kg}</p>
|
||||
<p className="font-semibold">Peso (kg)</p>
|
||||
<p>{patientDetails.weight_kg}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="font-semibold">Altura (m)</p>
|
||||
<p>{patientDetails.height_m}</p>
|
||||
<p className="font-semibold">Altura (m)</p>
|
||||
<p>{patientDetails.height_m}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="border-t pt-4 mt-4">
|
||||
<h3 className="font-semibold mb-2">Endereço</h3>
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div>
|
||||
<p className="font-semibold">Rua</p>
|
||||
<p>{`${patientDetails.street}, ${patientDetails.number}`}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="font-semibold">Complemento</p>
|
||||
<p>{patientDetails.complement}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="font-semibold">Bairro</p>
|
||||
<p>{patientDetails.neighborhood}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="font-semibold">Cidade</p>
|
||||
<p>{patientDetails.cidade}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="font-semibold">Estado</p>
|
||||
<p>{patientDetails.estado}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="font-semibold">CEP</p>
|
||||
<p>{patientDetails.cep}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="font-semibold">Rua</p>
|
||||
<p>{`${patientDetails.street}, ${patientDetails.number}`}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="font-semibold">Complemento</p>
|
||||
<p>{patientDetails.complement}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="font-semibold">Bairro</p>
|
||||
<p>{patientDetails.neighborhood}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="font-semibold">Cidade</p>
|
||||
<p>{patientDetails.cidade}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="font-semibold">Estado</p>
|
||||
<p>{patientDetails.estado}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="font-semibold">CEP</p>
|
||||
<p>{patientDetails.cep}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -147,12 +147,6 @@ export default function DoctorLayout({ children }: PatientLayoutProps) {
|
||||
label: "Consultas",
|
||||
// Botão para página de consultas marcadas do médico atual
|
||||
},
|
||||
{
|
||||
href: "/doctor/medicos/editorlaudo",
|
||||
icon: Clock,
|
||||
label: "Editor de Laudo",
|
||||
// Botão para página do editor de laudo
|
||||
},
|
||||
{
|
||||
href: "/doctor/medicos",
|
||||
icon: User,
|
||||
|
||||
@ -35,25 +35,26 @@ const buttonVariants = cva(
|
||||
},
|
||||
)
|
||||
|
||||
function Button({
|
||||
className,
|
||||
variant,
|
||||
size,
|
||||
asChild = false,
|
||||
...props
|
||||
}: React.ComponentProps<'button'> &
|
||||
VariantProps<typeof buttonVariants> & {
|
||||
asChild?: boolean
|
||||
}) {
|
||||
const Comp = asChild ? Slot : 'button'
|
||||
|
||||
return (
|
||||
<Comp
|
||||
data-slot="button"
|
||||
className={cn(buttonVariants({ variant, size, className }))}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
export interface ButtonProps
|
||||
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
|
||||
VariantProps<typeof buttonVariants> {
|
||||
asChild?: boolean
|
||||
}
|
||||
|
||||
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
||||
({ className, variant, size, asChild = false, ...props }, ref) => {
|
||||
const Comp = asChild ? Slot : 'button'
|
||||
|
||||
return (
|
||||
<Comp
|
||||
data-slot="button"
|
||||
className={cn(buttonVariants({ variant, size, className }))}
|
||||
ref={ref}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
)
|
||||
Button.displayName = 'Button'
|
||||
|
||||
export { Button, buttonVariants }
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user