From 634542dff79f77e80403c3ed6982702bc3ca2ada Mon Sep 17 00:00:00 2001 From: GagoDuBroca Date: Thu, 27 Nov 2025 11:17:54 -0300 Subject: [PATCH] =?UTF-8?q?Adi=C3=A7=C3=A3o=20de=20Barra=20de=20pesquisa?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/doctor/medicos/page.tsx | 456 +++++++++++++++++++----------------- 1 file changed, 240 insertions(+), 216 deletions(-) diff --git a/app/doctor/medicos/page.tsx b/app/doctor/medicos/page.tsx index 484ad9c..2d4e45a 100644 --- a/app/doctor/medicos/page.tsx +++ b/app/doctor/medicos/page.tsx @@ -2,8 +2,13 @@ import { useEffect, useState, useCallback } from "react"; import Link from "next/link"; -import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@/components/ui/dropdown-menu"; -import { Eye, Edit, Calendar, Trash2, Loader2, MoreVertical } from "lucide-react"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu"; +import { Eye, Edit, Calendar, Trash2, Loader2, MoreVertical, Filter } from "lucide-react"; import { api } from "@/services/api.mjs"; import { PatientDetailsModal } from "@/components/ui/patient-details-modal"; import { @@ -13,6 +18,7 @@ import { SelectTrigger, SelectValue, } from "@/components/ui/select"; +import { Input } from "@/components/ui/input"; import { Button } from "@/components/ui/button"; import Sidebar from "@/components/Sidebar"; @@ -35,6 +41,9 @@ interface Paciente { complement?: string; neighborhood?: string; cep?: string; + // NOVOS CAMPOS PARA O FILTRO + convenio?: string; + vip?: string; } export default function PacientesPage() { @@ -44,19 +53,44 @@ export default function PacientesPage() { const [selectedPatient, setSelectedPatient] = useState(null); const [isModalOpen, setIsModalOpen] = useState(false); - // --- Lógica de Paginação INÍCIO --- + // --- ESTADOS DOS FILTROS --- + const [searchTerm, setSearchTerm] = useState(""); + const [convenioFilter, setConvenioFilter] = useState("todos"); + const [vipFilter, setVipFilter] = useState("todos"); + + // --- Lógica de Filtragem --- + const filteredPacientes = pacientes.filter((p) => { + // 1. Filtro de Texto (Nome ou Telefone) + const searchLower = searchTerm.toLowerCase(); + const matchesSearch = p.nome?.toLowerCase().includes(searchLower) || p.telefone?.includes(searchLower); + + // 2. Filtro de Convênio + // Se for "todos", passa. Se não, verifica se o convênio do paciente é igual ao selecionado. + const matchesConvenio = convenioFilter === "todos" || (p.convenio?.toLowerCase() === convenioFilter); + + // 3. Filtro VIP + // Se for "todos", passa. Se não, verifica se o status VIP é igual ao selecionado. + const matchesVip = vipFilter === "todos" || (p.vip?.toLowerCase() === vipFilter); + + return matchesSearch && matchesConvenio && matchesVip; + }); + + // --- Lógica de Paginação --- const [itemsPerPage, setItemsPerPage] = useState(10); const [currentPage, setCurrentPage] = useState(1); - const totalPages = Math.ceil(pacientes.length / itemsPerPage); + // Resetar página quando qualquer filtro mudar + useEffect(() => { + setCurrentPage(1); + }, [searchTerm, convenioFilter, vipFilter, itemsPerPage]); + const totalPages = Math.ceil(filteredPacientes.length / itemsPerPage); const indexOfLastItem = currentPage * itemsPerPage; const indexOfFirstItem = indexOfLastItem - itemsPerPage; - const currentItems = pacientes.slice(indexOfFirstItem, indexOfLastItem); + const currentItems = filteredPacientes.slice(indexOfFirstItem, indexOfLastItem); const paginate = (pageNumber: number) => setCurrentPage(pageNumber); - // Funções de Navegação const goToPrevPage = () => { setCurrentPage((prev) => Math.max(1, prev - 1)); }; @@ -65,7 +99,6 @@ export default function PacientesPage() { setCurrentPage((prev) => Math.min(totalPages, prev + 1)); }; - // Lógica para gerar os números das páginas visíveis (máximo de 5) const getVisiblePageNumbers = (totalPages: number, currentPage: number) => { const pages: number[] = []; const maxVisiblePages = 5; @@ -90,12 +123,10 @@ export default function PacientesPage() { 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); }; - // --- Lógica de Paginação FIM --- const handleOpenModal = (patient: Paciente) => { setSelectedPatient(patient); @@ -113,7 +144,7 @@ export default function PacientesPage() { const date = new Date(dateString); return new Intl.DateTimeFormat("pt-BR").format(date); } catch (e) { - return dateString; // Retorna o string original se o formato for inválido + return dateString; } }; @@ -135,7 +166,7 @@ export default function PacientesPage() { cidade: p.city ?? "N/A", estado: p.state ?? "N/A", ultimoAtendimento: formatDate(p.created_at), - proximoAtendimento: "N/A", // Necessita de lógica de agendamento real + proximoAtendimento: "N/A", email: p.email ?? "N/A", birth_date: p.birth_date ?? "N/A", cpf: p.cpf ?? "N/A", @@ -147,10 +178,14 @@ export default function PacientesPage() { complement: p.complement ?? "N/A", neighborhood: p.neighborhood ?? "N/A", cep: p.cep ?? "N/A", + + // ⚠️ ATENÇÃO: Verifique o nome real desses campos na sua API + // Se a API não retorna, estou colocando valores padrão para teste + convenio: p.insurance_plan || p.convenio || "Unimed", // Exemplo: mapeie o campo correto + vip: p.is_vip ? "Sim" : "Não", // Exemplo: se for booleano converta para string })); setPacientes(mapped); - setCurrentPage(1); // Resetar a página ao carregar novos dados } catch (e: any) { console.error("Erro ao carregar pacientes:", e); setError(e?.message || "Erro ao carregar pacientes"); @@ -166,215 +201,206 @@ export default function PacientesPage() { return (
- {/* Cabeçalho */}
- {" "} - {/* Ajustado para flex-col em telas pequenas */}

Pacientes

Lista de pacientes vinculados

- {/* Controles de filtro e novo paciente */} - {/* Alterado para que o Select e o Link ocupem a largura total em telas pequenas e fiquem lado a lado em telas maiores */} -
- -
-
- {/* Tabela para Telas Médias e Grandes */} -
{/* Esconde em telas pequenas */} - - - - - - - - - - - - - - {loading ? ( - - - - ) : error ? ( - - - - ) : pacientes.length === 0 ? ( - - - - ) : ( - currentItems.map((p) => ( - - - - - - - - - - )) - )} - -
Nome - Telefone - - Cidade - - Estado - - Último atendimento - - Próximo atendimento - Ações
- - Carregando pacientes... -
{`Erro: ${error}`}
- Nenhum paciente encontrado -
{p.nome} - {p.telefone} - - {p.cidade} - - {p.estado} - - {p.ultimoAtendimento} - - {p.proximoAtendimento} - - - - - - - handleOpenModal(p)}> - - Ver detalhes - - - - - Laudos - - - {/* alert(`Agenda para paciente ID: ${p.id}`)}> - - Ver agenda - */} - {/* { - const newPacientes = pacientes.filter((pac) => pac.id !== p.id); - setPacientes(newPacientes); - alert(`Paciente ID: ${p.id} excluído`); - }} - className="text-red-600 focus:bg-red-50 focus:text-red-600" - > - - Excluir - */} - - -
-
+ {/* --- BARRA DE PESQUISA COM FILTROS ATIVOS --- */} +
+ + {/* Input de Busca */} +
+ + setSearchTerm(e.target.value)} + className="border-0 focus-visible:ring-0 shadow-none bg-transparent px-0 h-auto text-base placeholder:text-muted-foreground" + /> +
- {/* Layout em Cards/Lista para Telas Pequenas */} -
{/* Visível apenas em telas pequenas */} - {loading ? ( -
- - Carregando pacientes... -
- ) : error ? ( -
{`Erro: ${error}`}
- ) : pacientes.length === 0 ? ( -
- Nenhum paciente encontrado -
- ) : ( - currentItems.map((p) => ( -
-
{/* Adicionado padding à direita */} -
{/* Aumentado a fonte e break-words para evitar corte do nome */} - {p.nome || "—"} -
- {/* Removido o 'truncate' e adicionado 'break-words' no telefone */} -
- Telefone: **{p.telefone || "N/A"}** -
-
-
- - - - - - handleOpenModal(p)}> - - Ver detalhes - - - - - Laudos - - - alert(`Agenda para paciente ID: ${p.id}`)}> - - Ver agenda - - { - const newPacientes = pacientes.filter((pac) => pac.id !== p.id); - setPacientes(newPacientes); - alert(`Paciente ID: ${p.id} excluído`); - }} - className="text-red-600 focus:bg-red-50 focus:text-red-600" - > - - Excluir - - - -
-
- )) - )} -
+ {/* Filtros e Paginação */} +
+ + {/* FILTRO CONVÊNIO */} +
+ Convênio + +
+ {/* FILTRO VIP */} +
+ VIP + +
+ + {/* PAGINAÇÃO */} +
+ +
+ +
+
+ + {/* Tabela de Dados */} +
+
+ + + + + + {/* Coluna Convênio visível para teste */} + + + + + + + + {loading ? ( + + + + ) : error ? ( + + + + ) : filteredPacientes.length === 0 ? ( + + + + ) : ( + currentItems.map((p) => ( + + + + + + + + + )) + )} + +
NomeTelefoneConvênioVIPÚltimo atendimentoAções
+ + Carregando pacientes... +
{`Erro: ${error}`}
+ Nenhum paciente encontrado com esses filtros. +
{p.nome}{p.telefone}{p.convenio}{p.vip}{p.ultimoAtendimento} + + + + + + handleOpenModal(p)}> + + Ver detalhes + + + + + Laudos + + + + +
+
+ + {/* Cards para Mobile */} +
+ {loading ? ( +
+ + Carregando... +
+ ) : filteredPacientes.length === 0 ? ( +
+ Nenhum paciente encontrado. +
+ ) : ( + currentItems.map((p) => ( +
+
+
+ {p.nome || "—"} +
+
+ {p.telefone} | {p.convenio} | VIP: {p.vip} +
+
+
+ + + + + + handleOpenModal(p)}> + + Ver detalhes + + + + + Laudos + + + + +
+
+ )) + )} +
{/* Paginação */} {totalPages > 1 && (
- {/* Botão Anterior */}
- - - ); -} + + + ); +} \ No newline at end of file