diff --git a/susconecta/app/(main-routes)/consultas/page.tsx b/susconecta/app/(main-routes)/consultas/page.tsx index babede6..f351ff5 100644 --- a/susconecta/app/(main-routes)/consultas/page.tsx +++ b/susconecta/app/(main-routes)/consultas/page.tsx @@ -1,7 +1,7 @@ "use client"; import Link from "next/link"; -import { useEffect, useState, useCallback } from "react"; +import { useEffect, useState, useCallback, useMemo } from "react"; import { MoreHorizontal, PlusCircle, @@ -87,6 +87,10 @@ export default function ConsultasPage() { // Local form state used when editing. Keep hook at top-level to avoid Hooks order changes. const [localForm, setLocalForm] = useState(null); + // Paginação + const [currentPage, setCurrentPage] = useState(1); + const [itemsPerPage, setItemsPerPage] = useState(10); + const mapAppointmentToFormData = (appointment: any) => { // prefer scheduled_at (ISO) if available const scheduledBase = appointment.scheduled_at || appointment.time || appointment.created_at || null; @@ -177,8 +181,8 @@ export default function ConsultasPage() { let duration_minutes = 30; try { if (formData.startTime && formData.endTime) { - const [sh, sm] = String(formData.startTime).split(":").map((n: string) => Number(n)); - const [eh, em] = String(formData.endTime).split(":").map((n: string) => Number(n)); + const [sh, sm] = String(formData.startTime).split(":").map(Number); + const [eh, em] = String(formData.endTime).split(":").map(Number); const start = (sh || 0) * 60 + (sm || 0); const end = (eh || 0) * 60 + (em || 0); if (!Number.isNaN(start) && !Number.isNaN(end) && end > start) duration_minutes = end - start; @@ -404,12 +408,28 @@ export default function ConsultasPage() { performSearch(searchValue); }, 250); return () => clearTimeout(t); + // eslint-disable-next-line react-hooks/exhaustive-deps }, [searchValue, originalAppointments]); useEffect(() => { applyFilters(); + // eslint-disable-next-line react-hooks/exhaustive-deps }, [selectedStatus, filterDate, originalAppointments]); + // Dados paginados + const paginatedAppointments = useMemo(() => { + const startIndex = (currentPage - 1) * itemsPerPage; + const endIndex = startIndex + itemsPerPage; + return appointments.slice(startIndex, endIndex); + }, [appointments, currentPage, itemsPerPage]); + + const totalPages = Math.ceil(appointments.length / itemsPerPage); + + // Reset para página 1 quando mudar a busca ou itens por página + useEffect(() => { + setCurrentPage(1); + }, [searchValue, selectedStatus, filterDate, itemsPerPage]); + // Keep localForm synchronized with editingAppointment useEffect(() => { if (showForm && editingAppointment) { @@ -515,7 +535,7 @@ export default function ConsultasPage() { - {appointments.map((appointment) => { + {paginatedAppointments.map((appointment) => { // appointment.professional may now contain the doctor's name (resolved) const professionalLookup = mockProfessionals.find((p) => p.id === appointment.professional); const professionalName = typeof appointment.professional === "string" && appointment.professional && !professionalLookup @@ -574,6 +594,64 @@ export default function ConsultasPage() { + {/* Controles de paginação */} +
+
+ Itens por página: + + + Mostrando {paginatedAppointments.length > 0 ? (currentPage - 1) * itemsPerPage + 1 : 0} a{" "} + {Math.min(currentPage * itemsPerPage, appointments.length)} de {appointments.length} + +
+ +
+ + + + Página {currentPage} de {totalPages || 1} + + + +
+
+ {viewingAppointment && ( setViewingAppointment(null)}> diff --git a/susconecta/app/(main-routes)/doutores/page.tsx b/susconecta/app/(main-routes)/doutores/page.tsx index 1acbb83..85a795e 100644 --- a/susconecta/app/(main-routes)/doutores/page.tsx +++ b/susconecta/app/(main-routes)/doutores/page.tsx @@ -141,6 +141,10 @@ export default function DoutoresPage() { const [searchMode, setSearchMode] = useState(false); const [searchTimeout, setSearchTimeout] = useState(null); + // Paginação + const [currentPage, setCurrentPage] = useState(1); + const [itemsPerPage, setItemsPerPage] = useState(10); + async function load() { setLoading(true); @@ -310,6 +314,20 @@ export default function DoutoresPage() { return filtered; }, [doctors, search, searchMode, searchResults]); + // Dados paginados + const paginatedDoctors = useMemo(() => { + const startIndex = (currentPage - 1) * itemsPerPage; + const endIndex = startIndex + itemsPerPage; + return displayedDoctors.slice(startIndex, endIndex); + }, [displayedDoctors, currentPage, itemsPerPage]); + + const totalPages = Math.ceil(displayedDoctors.length / itemsPerPage); + + // Reset para página 1 quando mudar a busca ou itens por página + useEffect(() => { + setCurrentPage(1); + }, [search, itemsPerPage, searchMode]); + function handleAdd() { setEditingId(null); setShowForm(true); @@ -480,8 +498,8 @@ export default function DoutoresPage() { Carregando… - ) : displayedDoctors.length > 0 ? ( - displayedDoctors.map((doctor) => ( + ) : paginatedDoctors.length > 0 ? ( + paginatedDoctors.map((doctor) => ( {doctor.full_name} @@ -580,6 +598,64 @@ export default function DoutoresPage() { + {/* Controles de paginação */} +
+
+ Itens por página: + + + Mostrando {paginatedDoctors.length > 0 ? (currentPage - 1) * itemsPerPage + 1 : 0} a{" "} + {Math.min(currentPage * itemsPerPage, displayedDoctors.length)} de {displayedDoctors.length} + +
+ +
+ + + + Página {currentPage} de {totalPages || 1} + + + +
+
+ {viewingDoctor && ( setViewingDoctor(null)}> @@ -753,7 +829,7 @@ export default function DoutoresPage() { )}
- Mostrando {displayedDoctors.length} {searchMode ? 'resultado(s) da busca' : `de ${doctors.length}`} + {searchMode ? 'Resultado(s) da busca' : `Total de ${doctors.length} médico(s)`}
{/* Dialog para pacientes atribuídos */} { if (!open) { setAssignedDialogOpen(false); setAssignedPatients([]); setAssignedDoctor(null); } }}> diff --git a/susconecta/app/(main-routes)/pacientes/page.tsx b/susconecta/app/(main-routes)/pacientes/page.tsx index c09855e..fe592e9 100644 --- a/susconecta/app/(main-routes)/pacientes/page.tsx +++ b/susconecta/app/(main-routes)/pacientes/page.tsx @@ -49,6 +49,10 @@ export default function PacientesPage() { const [viewingPatient, setViewingPatient] = useState(null); const [assignDialogOpen, setAssignDialogOpen] = useState(false); const [assignPatientId, setAssignPatientId] = useState(null); + + // Paginação + const [currentPage, setCurrentPage] = useState(1); + const [itemsPerPage, setItemsPerPage] = useState(10); async function loadAll() { try { @@ -95,6 +99,20 @@ export default function PacientesPage() { }); }, [patients, search]); + // Dados paginados + const paginatedData = useMemo(() => { + const startIndex = (currentPage - 1) * itemsPerPage; + const endIndex = startIndex + itemsPerPage; + return filtered.slice(startIndex, endIndex); + }, [filtered, currentPage, itemsPerPage]); + + const totalPages = Math.ceil(filtered.length / itemsPerPage); + + // Reset para página 1 quando mudar a busca ou itens por página + useEffect(() => { + setCurrentPage(1); + }, [search, itemsPerPage]); + function handleAdd() { setEditingId(null); setShowForm(true); @@ -228,8 +246,8 @@ export default function PacientesPage() {
- {filtered.length > 0 ? ( - filtered.map((p) => ( + {paginatedData.length > 0 ? ( + paginatedData.map((p) => ( {p.full_name || "(sem nome)"} {p.cpf || "-"} @@ -277,6 +295,64 @@ export default function PacientesPage() { + {/* Controles de paginação */} +
+
+ Itens por página: + + + Mostrando {paginatedData.length > 0 ? (currentPage - 1) * itemsPerPage + 1 : 0} a{" "} + {Math.min(currentPage * itemsPerPage, filtered.length)} de {filtered.length} + +
+ +
+ + + + Página {currentPage} de {totalPages || 1} + + + +
+
+ {viewingPatient && ( setViewingPatient(null)}> @@ -326,8 +402,6 @@ export default function PacientesPage() { onSaved={() => { setAssignDialogOpen(false); setAssignPatientId(null); loadAll(); }} /> )} - -
Mostrando {filtered.length} de {patients.length}
); }