re-alteracoes esteticas

This commit is contained in:
pedrosiimoes 2025-11-06 00:18:34 -03:00
parent 1aec6b56d0
commit 1daa664ff4
3 changed files with 442 additions and 449 deletions

View File

@ -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, MoreVertical, Loader2 } from "lucide-react"
import { Plus, Edit, Trash2, Eye, Calendar, Filter, Loader2 } from "lucide-react"
import {
AlertDialog,
AlertDialogAction,
@ -69,24 +69,20 @@ export default function DoctorsPage() {
const [specialtyFilter, setSpecialtyFilter] = useState("all");
const [statusFilter, setStatusFilter] = useState("all");
// --- Estados para Paginação (ADICIONADOS) ---
// --- Estados para Paginação ---
const [itemsPerPage, setItemsPerPage] = useState(10);
const [currentPage, setCurrentPage] = useState(1);
// ---------------------------------------------
const fetchDoctors = useCallback(async () => {
setLoading(true);
setError(null);
try {
const data: Doctor[] = await doctorsService.list();
// Exemplo: Adicionando um status fake
const dataWithStatus = data.map((doc, index) => ({
...doc,
status: index % 3 === 0 ? "Inativo" : index % 2 === 0 ? "Férias" : "Ativo"
}));
setDoctors(dataWithStatus || []);
// IMPORTANTE: Resetar a página ao carregar novos dados
setCurrentPage(1);
} catch (e: any) {
console.error("Erro ao carregar lista de médicos:", e);
@ -141,37 +137,23 @@ export default function DoctorsPage() {
setDeleteDialogOpen(true);
};
const handleEdit = (doctorId: number) => {
router.push(`/manager/home/${doctorId}/editar`);
};
// Gera a lista de especialidades dinamicamente
const uniqueSpecialties = useMemo(() => {
const specialties = doctors.map(doctor => doctor.specialty).filter(Boolean);
return [...new Set(specialties)];
}, [doctors]);
// Lógica de filtragem
const filteredDoctors = doctors.filter(doctor => {
const specialtyMatch = specialtyFilter === "all" || doctor.specialty === specialtyFilter;
const statusMatch = statusFilter === "all" || doctor.status === statusFilter;
return specialtyMatch && statusMatch;
});
// --- Lógica de Paginação (ADICIONADA) ---
// 1. Definição do total de páginas com base nos itens FILTRADOS
const totalPages = Math.ceil(filteredDoctors.length / itemsPerPage);
// 2. Cálculo dos itens a serem exibidos na página atual (dos itens FILTRADOS)
const indexOfLastItem = currentPage * itemsPerPage;
const indexOfFirstItem = indexOfLastItem - itemsPerPage;
const currentItems = filteredDoctors.slice(indexOfFirstItem, indexOfLastItem);
// 3. Função para mudar de página
const paginate = (pageNumber: number) => setCurrentPage(pageNumber);
// 4. Funções para Navegação
const goToPrevPage = () => {
setCurrentPage((prev) => Math.max(1, prev - 1));
};
@ -180,7 +162,6 @@ export default function DoctorsPage() {
setCurrentPage((prev) => Math.min(totalPages, prev + 1));
};
// 5. Lógica para gerar os números das páginas visíveis
const getVisiblePageNumbers = (totalPages: number, currentPage: number) => {
const pages: number[] = [];
const maxVisiblePages = 5;
@ -205,12 +186,10 @@ export default function DoctorsPage() {
const visiblePageNumbers = getVisiblePageNumbers(totalPages, currentPage);
// Lógica para mudar itens por página, resetando para a página 1
const handleItemsPerPageChange = (value: string) => {
setItemsPerPage(Number(value));
setCurrentPage(1); // Resetar para a primeira página
setCurrentPage(1);
};
// ----------------------------------------------------
return (
@ -232,11 +211,12 @@ export default function DoctorsPage() {
</div>
{/* Filtros e Itens por Página (ATUALIZADO) */}
{/* Filtros e Itens por Página */}
<div className="flex flex-wrap items-center gap-3 bg-white p-3 sm:p-4 rounded-lg border border-gray-200">
<Filter className="w-5 h-5 text-gray-400" />
{/* Filtro de Especialidade */}
<div className="flex items-center gap-2 w-full md:w-auto">
<span className="text-sm font-medium text-foreground">
Especialidade
</span>
<Select value={specialtyFilter} onValueChange={setSpecialtyFilter}>
<SelectTrigger className="w-[160px] sm:w-[180px]">
<SelectValue placeholder="Especialidade" />
@ -248,8 +228,11 @@ export default function DoctorsPage() {
))}
</SelectContent>
</Select>
{/* Filtro de Status */}
</div>
<div className="flex items-center gap-2 w-full md:w-auto">
<span className="text-sm font-medium text-foreground">
Status
</span>
<Select value={statusFilter} onValueChange={setStatusFilter}>
<SelectTrigger className="w-[160px] sm:w-[180px]">
<SelectValue placeholder="Status" />
@ -261,12 +244,14 @@ export default function DoctorsPage() {
<SelectItem value="Inativo">Inativo</SelectItem>
</SelectContent>
</Select>
{/* Select de Itens por Página (ADICIONADO) */}
</div>
<div className="flex items-center gap-2 w-full md:w-auto">
<span className="text-sm font-medium text-foreground">
Itens por página
</span>
<Select
onValueChange={handleItemsPerPageChange}
defaultValue={String(itemsPerPage)}
>
defaultValue={String(itemsPerPage)}>
<SelectTrigger className="w-[140px]">
<SelectValue placeholder="Itens por pág." />
</SelectTrigger>
@ -276,7 +261,7 @@ export default function DoctorsPage() {
<SelectItem value="20">20 por página</SelectItem>
</SelectContent>
</Select>
</div>
<Button variant="outline" className="ml-auto w-full md:w-auto">
<Filter className="w-4 h-4 mr-2" />
Filtro avançado
@ -302,19 +287,18 @@ export default function DoctorsPage() {
</div>
) : (
<div className="overflow-x-auto">
<table className="min-w-[700px] w-full divide-y divide-gray-200 text-sm sm:text-base">
<thead className="bg-gray-50">
<table className="w-full min-w-[600px]">
<thead className="bg-gray-50 border-b border-gray-200">
<tr>
<th className="px-4 py-3 text-left font-medium text-gray-500 uppercase tracking-wider">Nome</th>
<th className="px-4 py-3 text-left font-medium text-gray-500 uppercase tracking-wider hidden sm:table-cell">CRM</th>
<th className="px-4 py-3 text-left font-medium text-gray-500 uppercase tracking-wider hidden md:table-cell">Especialidade</th>
<th className="px-4 py-3 text-left font-medium text-gray-500 uppercase tracking-wider hidden lg:table-cell">Status</th>
<th className="px-4 py-3 text-left font-medium text-gray-500 uppercase tracking-wider hidden xl:table-cell">Cidade/Estado</th>
<th className="px-4 py-3 text-right font-medium text-gray-500 uppercase tracking-wider">Ações</th>
<th className="text-left p-2 md:p-4 font-medium text-gray-700">Nome</th>
<th className="text-left p-2 md:p-4 font-medium text-gray-700">CRM</th>
<th className="text-left p-2 md:p-4 font-medium text-gray-700">Especialidade</th>
<th className="text-left p-2 md:p-4 font-medium text-gray-700">Status</th>
<th className="text-left p-2 md:p-4 font-medium text-gray-700">Cidade/Estado</th>
<th className="text-right p-4 font-medium text-gray-700">Ações</th>
</tr>
</thead>
<tbody className="bg-white divide-y divide-gray-200">
{/* Mapeia APENAS os itens da página atual (currentItems) */}
{currentItems.map((doctor) => (
<tr key={doctor.id} className="hover:bg-gray-50 transition">
<td className="px-4 py-3 font-medium text-gray-900">{doctor.full_name}</td>
@ -327,19 +311,33 @@ export default function DoctorsPage() {
: "N/A"}
</td>
<td className="px-4 py-3 text-right">
<div className="flex justify-end gap-2">
<Button variant="outline" size="icon" onClick={() => openDetailsDialog(doctor)} title="Visualizar Detalhes"><Eye className="h-4 w-4" /></Button>
<Button variant="outline" size="icon" onClick={() => handleEdit(doctor.id)} title="Editar"><Edit className="h-4 w-4 text-blue-600" /></Button>
<Button variant="outline" size="icon" onClick={() => openDeleteDialog(doctor.id)} title="Excluir"><Trash2 className="h-4 w-4 text-red-600" /></Button>
{/* ===== INÍCIO DA ALTERAÇÃO ===== */}
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="ghost" className="h-8 w-8 p-0" title="Mais Ações"><MoreVertical className="h-4 w-4" /></Button>
<div className="text-blue-600 cursor-pointer inline-block">Ações</div>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuItem><Calendar className="mr-2 h-4 w-4" />Agendar Consulta</DropdownMenuItem>
<DropdownMenuItem onClick={() => openDetailsDialog(doctor)}>
<Eye className="mr-2 h-4 w-4" />
Ver detalhes
</DropdownMenuItem>
<DropdownMenuItem asChild>
<Link href={`/manager/home/${doctor.id}/editar`}>
<Edit className="mr-2 h-4 w-4" />
Editar
</Link>
</DropdownMenuItem>
<DropdownMenuItem>
<Calendar className="mr-2 h-4 w-4" />
Marcar consulta
</DropdownMenuItem>
<DropdownMenuItem className="text-red-600" onClick={() => openDeleteDialog(doctor.id)}>
<Trash2 className="mr-2 h-4 w-4" />
Excluir
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</div>
{/* ===== FIM DA ALTERAÇÃO ===== */}
</td>
</tr>
))}
@ -349,11 +347,9 @@ export default function DoctorsPage() {
)}
</div>
{/* Paginação (ADICIONADA) */}
{/* Paginação */}
{totalPages > 1 && (
<div className="flex flex-wrap justify-center items-center gap-2 mt-4 p-4 bg-white rounded-lg border border-gray-200 shadow-md">
{/* Botão Anterior */}
<button
onClick={goToPrevPage}
disabled={currentPage === 1}
@ -362,13 +358,11 @@ export default function DoctorsPage() {
{"< Anterior"}
</button>
{/* Números das Páginas */}
{visiblePageNumbers.map((number) => (
<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"
}`}
@ -377,7 +371,6 @@ export default function DoctorsPage() {
</button>
))}
{/* Botão Próximo */}
<button
onClick={goToNextPage}
disabled={currentPage === totalPages}
@ -385,11 +378,10 @@ export default function DoctorsPage() {
>
{"Próximo >"}
</button>
</div>
)}
{/* Dialogs de Exclusão e Detalhes (Sem alterações) */}
{/* Dialogs de Exclusão e Detalhes */}
<AlertDialog open={deleteDialogOpen} onOpenChange={setDeleteDialogOpen}>
<AlertDialogContent>
<AlertDialogHeader>

View File

@ -1,6 +1,6 @@
"use client";
import { useState, useEffect, useRef, useCallback } from "react";
import React, { useState, useEffect, useCallback, useMemo } from "react";
import Link from "next/link";
import { Button } from "@/components/ui/button";
import {
@ -16,7 +16,7 @@ import {
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import { Plus, Edit, Trash2, Eye, Calendar, Filter } from "lucide-react";
import { Plus, Edit, Trash2, Eye, Calendar, Filter, Loader2 } from "lucide-react";
import {
AlertDialog,
AlertDialogAction,
@ -30,29 +30,22 @@ import {
import ManagerLayout from "@/components/manager-layout";
import { patientsService } from "@/services/patientsApi.mjs";
// 📅 PASSO 1: Criar uma função para formatar a data
const formatDate = (dateString: string | null | undefined): string => {
// Se a data não existir, retorna um texto padrão
if (!dateString) {
return "N/A";
}
try {
const date = new Date(dateString);
// Verifica se a data é válida após a conversão
if (isNaN(date.getTime())) {
return "Data inválida";
}
const day = String(date.getDate()).padStart(2, '0');
const month = String(date.getMonth() + 1).padStart(2, '0'); // Mês é base 0, então +1
const month = String(date.getMonth() + 1).padStart(2, '0');
const year = date.getFullYear();
const hours = String(date.getHours()).padStart(2, '0');
const minutes = String(date.getMinutes()).padStart(2, '0');
return `${day}/${month}/${year} ${hours}:${minutes}`;
} catch (error) {
// Se houver qualquer erro na conversão, retorna um texto de erro
return "Data inválida";
}
};
@ -63,16 +56,19 @@ export default function PacientesPage() {
const [convenioFilter, setConvenioFilter] = useState("all");
const [vipFilter, setVipFilter] = useState("all");
const [patients, setPatients] = useState<any[]>([]);
const [loading, setLoading] = useState(false);
const [loading, setLoading] = useState(true); // Alterado para true
const [error, setError] = useState<string | null>(null);
const [page, setPage] = useState(1);
const [hasNext, setHasNext] = useState(true);
const [isFetching, setIsFetching] = useState(false);
const observerRef = useRef<HTMLDivElement | null>(null);
// --- Estados de Paginação (ADICIONADOS) ---
const [itemsPerPage, setItemsPerPage] = useState(10);
const [currentPage, setCurrentPage] = useState(1);
// ---------------------------------------------
const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);
const [patientToDelete, setPatientToDelete] = useState<string | null>(null);
const [detailsDialogOpen, setDetailsDialogOpen] = useState(false);
const [patientDetails, setPatientDetails] = useState<any | null>(null);
const openDetailsDialog = async (patientId: string) => {
setDetailsDialogOpen(true);
setPatientDetails(null);
@ -84,10 +80,9 @@ export default function PacientesPage() {
}
};
const fetchPacientes = useCallback(
async (pageToFetch: number) => {
if (isFetching || !hasNext) return;
setIsFetching(true);
// --- LÓGICA DE BUSCA DE DADOS (ATUALIZADA) ---
const fetchPacientes = useCallback(async () => {
setLoading(true);
setError(null);
try {
const res = await patientsService.list();
@ -100,59 +95,28 @@ export default function PacientesPage() {
ultimoAtendimento: p.last_visit_at ?? "",
proximoAtendimento: p.next_appointment_at ?? "",
vip: Boolean(p.vip ?? false),
convenio: p.convenio ?? "", // se não existir, fica vazio
convenio: p.convenio ?? "",
status: p.status ?? undefined,
}));
setPatients((prev) => {
const all = [...prev, ...mapped];
const unique = Array.from(
new Map(all.map((p) => [p.id, p])).values()
);
return unique;
});
if (!mapped.id) setHasNext(false); // parar carregamento
else setPage((prev) => prev + 1);
setPatients(mapped);
setCurrentPage(1); // Resetar para a primeira página ao carregar
} catch (e: any) {
setError(e?.message || "Erro ao buscar pacientes");
setPatients([]);
} finally {
setIsFetching(false);
setLoading(false);
}
},
[isFetching, hasNext]
);
useEffect(() => {
fetchPacientes(page);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
useEffect(() => {
if (!observerRef.current || !hasNext) return;
const observer = new window.IntersectionObserver((entries) => {
if (entries[0].isIntersecting && !isFetching && hasNext) {
fetchPacientes(page);
}
});
observer.observe(observerRef.current);
return () => {
if (observerRef.current) observer.unobserve(observerRef.current);
};
}, [fetchPacientes, page, hasNext, isFetching]);
fetchPacientes();
}, [fetchPacientes]);
const handleDeletePatient = async (patientId: string) => {
// Remove from current list (client-side deletion)
try {
const res = await patientsService.delete(patientId);
if (res) {
alert(`${res.error} ${res.message}`);
}
setPatients((prev) =>
prev.filter((p) => String(p.id) !== String(patientId))
);
await patientsService.delete(patientId);
// Recarrega a lista para refletir a exclusão
await fetchPacientes();
} catch (e: any) {
setError(e?.message || "Erro ao deletar paciente");
}
@ -179,6 +143,50 @@ export default function PacientesPage() {
return matchesSearch && matchesConvenio && matchesVip;
});
// --- LÓGICA DE PAGINAÇÃO (ADICIONADA) ---
const totalPages = Math.ceil(filteredPatients.length / itemsPerPage);
const indexOfLastItem = currentPage * itemsPerPage;
const indexOfFirstItem = indexOfLastItem - itemsPerPage;
const currentItems = filteredPatients.slice(indexOfFirstItem, indexOfLastItem);
const paginate = (pageNumber: number) => setCurrentPage(pageNumber);
const goToPrevPage = () => {
setCurrentPage((prev) => Math.max(1, prev - 1));
};
const goToNextPage = () => {
setCurrentPage((prev) => Math.min(totalPages, prev + 1));
};
const getVisiblePageNumbers = (totalPages: number, currentPage: number) => {
const pages: number[] = [];
const maxVisiblePages = 5;
const halfRange = Math.floor(maxVisiblePages / 2);
let startPage = Math.max(1, currentPage - halfRange);
let endPage = Math.min(totalPages, currentPage + halfRange);
if (endPage - startPage + 1 < maxVisiblePages) {
if (endPage === totalPages) {
startPage = Math.max(1, totalPages - maxVisiblePages + 1);
}
if (startPage === 1) {
endPage = Math.min(totalPages, maxVisiblePages);
}
}
for (let i = startPage; i <= endPage; i++) {
pages.push(i);
}
return pages;
};
const visiblePageNumbers = getVisiblePageNumbers(totalPages, currentPage);
const handleItemsPerPageChange = (value: string) => {
setItemsPerPage(Number(value));
setCurrentPage(1); // Resetar para a primeira página
};
// ---------------------------------------------
return (
<ManagerLayout>
<div className="space-y-6">
@ -193,16 +201,15 @@ export default function PacientesPage() {
</div>
<div className="flex gap-2">
<Link href="/manager/pacientes/novo">
<Button className="w-full md:w-auto">
<Button className="w-full md:w-auto bg-green-600 hover:bg-green-700">
<Plus className="w-4 h-4 mr-2" />
Adicionar
Adicionar Novo
</Button>
</Link>
</div>
</div>
<div className="flex flex-col md:flex-row flex-wrap gap-4 bg-card p-4 rounded-lg border border-border">
{/* Convênio */}
<div className="flex items-center gap-2 w-full md:w-auto">
<span className="text-sm font-medium text-foreground">
Convênio
@ -233,18 +240,18 @@ export default function PacientesPage() {
</SelectContent>
</Select>
</div>
{/* SELETOR DE ITENS POR PÁGINA (ADICIONADO) */}
<div className="flex items-center gap-2 w-full md:w-auto">
<span className="text-sm font-medium text-foreground">
Aniversariantes
</span>
<Select>
<span className="text-sm font-medium text-foreground">Itens por página</span>
<Select onValueChange={handleItemsPerPageChange} defaultValue={String(itemsPerPage)}>
<SelectTrigger className="w-full md:w-32">
<SelectValue placeholder="Selecione" />
<SelectValue placeholder="Itens por pág." />
</SelectTrigger>
<SelectContent>
<SelectItem value="today">Hoje</SelectItem>
<SelectItem value="week">Esta semana</SelectItem>
<SelectItem value="month">Este mês</SelectItem>
<SelectItem value="5">5 por página</SelectItem>
<SelectItem value="10">10 por página</SelectItem>
<SelectItem value="20">20 por página</SelectItem>
</SelectContent>
</Select>
</div>
@ -257,30 +264,34 @@ export default function PacientesPage() {
<div className="bg-white rounded-lg border border-gray-200">
<div className="overflow-x-auto">
{error ? (
{loading ? (
<div className="p-8 text-center text-gray-500">
<Loader2 className="w-8 h-8 animate-spin mx-auto mb-3 text-green-600" />
Carregando pacientes...
</div>
) : error ? (
<div className="p-6 text-red-600">{`Erro ao carregar pacientes: ${error}`}</div>
) : (
<table className="w-full min-w-[600px]">
<thead className="bg-gray-50 border-b border-gray-200">
<tr>
<th className="text-left p-2 md:p-4 font-medium text-gray-700">Nome</th>
<th className="text-left p-2 md:p-4 font-medium text-gray-700">Telefone</th>
<th className="text-left p-2 md:p-4 font-medium text-gray-700">Cidade</th>
<th className="text-left p-2 md:p-4 font-medium text-gray-700">Estado</th>
<th className="text-left p-2 md:p-4 font-medium text-gray-700">Último atendimento</th>
<th className="text-left p-2 md:p-4 font-medium text-gray-700">Próximo atendimento</th>
<th className="text-left p-2 md:p-4 font-medium text-gray-700">Ações</th>
<th className="text-left p-4 font-medium text-gray-700">Nome</th>
<th className="text-left p-4 font-medium text-gray-700">Telefone</th>
<th className="text-left p-4 font-medium text-gray-700">Cidade</th>
<th className="text-left p-4 font-medium text-gray-700">Último Atendimento</th>
<th className="text-right p-4 font-medium text-gray-700">Ações</th>
</tr>
</thead>
<tbody>
{filteredPatients.length === 0 ? (
<tr>
<td colSpan={7} className="p-8 text-center text-gray-500">
{patients.length === 0 ? "Nenhum paciente cadastrado" : "Nenhum paciente encontrado com os filtros aplicados"}
<td colSpan={5} className="p-8 text-center text-gray-500">
{patients.length === 0 ? "Nenhum paciente cadastrado." : "Nenhum paciente encontrado com os filtros aplicados."}
</td>
</tr>
) : (
filteredPatients.map((patient) => (
// Mapeando `currentItems` em vez de `filteredPatients`
currentItems.map((patient) => (
<tr key={patient.id} className="border-b border-gray-100 hover:bg-gray-50">
<td className="p-4">
<div className="flex items-center gap-3">
@ -292,14 +303,11 @@ export default function PacientesPage() {
</td>
<td className="p-4 text-gray-600">{patient.telefone}</td>
<td className="p-4 text-gray-600">{patient.cidade}</td>
<td className="p-4 text-gray-600">{patient.estado}</td>
{/* 📅 PASSO 2: Aplicar a formatação de data na tabela */}
<td className="p-4 text-gray-600">{formatDate(patient.ultimoAtendimento)}</td>
<td className="p-4 text-gray-600">{formatDate(patient.proximoAtendimento)}</td>
<td className="p-4">
<td className="p-4 text-right">
<DropdownMenu>
<DropdownMenuTrigger asChild>
<div className="text-blue-600 cursor-pointer">Ações</div>
<div className="text-blue-600 cursor-pointer inline-block">Ações</div>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuItem onClick={() => openDetailsDialog(String(patient.id))}>
@ -329,11 +337,44 @@ export default function PacientesPage() {
</tbody>
</table>
)}
<div ref={observerRef} style={{ height: 1 }} />
{isFetching && <div className="p-4 text-center text-gray-500">Carregando mais pacientes...</div>}
</div>
</div>
{/* COMPONENTE DE PAGINAÇÃO (ADICIONADO) */}
{totalPages > 1 && (
<div className="flex flex-wrap justify-center items-center gap-2 mt-4 p-4 bg-white rounded-lg border border-gray-200 shadow-md">
<button
onClick={goToPrevPage}
disabled={currentPage === 1}
className="flex items-center px-4 py-2 rounded-md font-medium transition-colors text-sm bg-gray-100 text-gray-700 hover:bg-gray-200 disabled:opacity-50 disabled:cursor-not-allowed border border-gray-300"
>
{"< Anterior"}
</button>
{visiblePageNumbers.map((number) => (
<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
? "bg-green-600 text-white shadow-md border-green-600"
: "bg-gray-100 text-gray-700 hover:bg-gray-200"
}`}
>
{number}
</button>
))}
<button
onClick={goToNextPage}
disabled={currentPage === totalPages}
className="flex items-center px-4 py-2 rounded-md font-medium transition-colors text-sm bg-gray-100 text-gray-700 hover:bg-gray-200 disabled:opacity-50 disabled:cursor-not-allowed border border-gray-300"
>
{"Próximo >"}
</button>
</div>
)}
{/* MODAIS (SEM ALTERAÇÃO) */}
<AlertDialog open={deleteDialogOpen} onOpenChange={setDeleteDialogOpen}>
<AlertDialogContent>
<AlertDialogHeader>
@ -357,7 +398,6 @@ export default function PacientesPage() {
</AlertDialogContent>
</AlertDialog>
{/* Modal de detalhes do paciente */}
<AlertDialog open={detailsDialogOpen} onOpenChange={setDetailsDialogOpen}>
<AlertDialogContent>
<AlertDialogHeader>
@ -369,63 +409,12 @@ export default function PacientesPage() {
<div className="text-red-600">{patientDetails.error}</div>
) : (
<div className="space-y-2 text-left">
<p>
<strong>Nome:</strong> {patientDetails.full_name}
</p>
<p>
<strong>CPF:</strong> {patientDetails.cpf}
</p>
<p>
<strong>Email:</strong> {patientDetails.email}
</p>
<p>
<strong>Telefone:</strong> {patientDetails.phone_mobile ?? patientDetails.phone1 ?? patientDetails.phone2 ?? "-"}
</p>
<p>
<strong>Nome social:</strong> {patientDetails.social_name ?? "-"}
</p>
<p>
<strong>Sexo:</strong> {patientDetails.sex ?? "-"}
</p>
<p>
<strong>Tipo sanguíneo:</strong> {patientDetails.blood_type ?? "-"}
</p>
<p>
<strong>Peso:</strong> {patientDetails.weight_kg ?? "-"}
{patientDetails.weight_kg ? "kg" : ""}
</p>
<p>
<strong>Altura:</strong> {patientDetails.height_m ?? "-"}
{patientDetails.height_m ? "m" : ""}
</p>
<p>
<strong>IMC:</strong> {patientDetails.bmi ?? "-"}
</p>
<p>
<strong>Endereço:</strong> {patientDetails.street ?? "-"}
</p>
<p>
<strong>Bairro:</strong> {patientDetails.neighborhood ?? "-"}
</p>
<p>
<strong>Cidade:</strong> {patientDetails.city ?? "-"}
</p>
<p>
<strong>Estado:</strong> {patientDetails.state ?? "-"}
</p>
<p>
<strong>CEP:</strong> {patientDetails.cep ?? "-"}
</p>
{/* 📅 PASSO 3: Aplicar a formatação de data no modal */}
<p>
<strong>Criado em:</strong> {formatDate(patientDetails.created_at)}
</p>
<p>
<strong>Atualizado em:</strong> {formatDate(patientDetails.updated_at)}
</p>
<p>
<strong>Id:</strong> {patientDetails.id ?? "-"}
</p>
<p><strong>Nome:</strong> {patientDetails.full_name}</p>
<p><strong>CPF:</strong> {patientDetails.cpf}</p>
<p><strong>Email:</strong> {patientDetails.email}</p>
<p><strong>Telefone:</strong> {patientDetails.phone_mobile ?? patientDetails.phone1 ?? patientDetails.phone2 ?? "-"}</p>
<p><strong>Endereço:</strong> {`${patientDetails.street ?? "-"}, ${patientDetails.neighborhood ?? "-"}, ${patientDetails.city ?? "-"} - ${patientDetails.state ?? "-"}`}</p>
<p><strong>Criado em:</strong> {formatDate(patientDetails.created_at)}</p>
</div>
)}
</AlertDialogDescription>

View File

@ -210,16 +210,19 @@ export default function UsersPage() {
{/* Filtro e Itens por Página */}
<div className="flex flex-wrap items-center gap-3 bg-white p-4 rounded-lg border border-gray-200">
<Filter className="w-5 h-5 text-gray-400" />
{/* Select de Filtro por Papel - Ajustado para resetar a página */}
<div className="flex items-center gap-2 w-full md:w-auto">
<span className="text-sm font-medium text-foreground">
Filtrar por papel
</span>
<Select
onValueChange={(value) => {
setSelectedRole(value);
setCurrentPage(1); // Resetar para a primeira página ao mudar o filtro
}}
value={selectedRole}
>
value={selectedRole}>
<SelectTrigger className="w-[180px]">
<SelectValue placeholder="Filtrar por papel" />
</SelectTrigger>
@ -232,8 +235,13 @@ export default function UsersPage() {
<SelectItem value="user">Usuário</SelectItem>
</SelectContent>
</Select>
</div>
{/* Select de Itens por Página */}
<div className="flex items-center gap-2 w-full md:w-auto">
<span className="text-sm font-medium text-foreground">
Itens por página
</span>
<Select
onValueChange={handleItemsPerPageChange}
defaultValue={String(itemsPerPage)}
@ -248,6 +256,11 @@ export default function UsersPage() {
</SelectContent>
</Select>
</div>
<Button variant="outline" className="ml-auto w-full md:w-auto">
<Filter className="w-4 h-4 mr-2" />
Filtro avançado
</Button>
</div>
{/* Fim do Filtro e Itens por Página */}
{/* Tabela */}
@ -331,8 +344,7 @@ export default function UsersPage() {
<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"
}`}