diff --git a/app/doctor/medicos/[id]/laudos/[laudoId]/editar/page.tsx b/app/doctor/medicos/[id]/laudos/[laudoId]/editar/page.tsx index 52525fd..acce902 100644 --- a/app/doctor/medicos/[id]/laudos/[laudoId]/editar/page.tsx +++ b/app/doctor/medicos/[id]/laudos/[laudoId]/editar/page.tsx @@ -144,10 +144,6 @@ export default function EditarLaudoPage() {
-
- - -
diff --git a/app/doctor/medicos/[id]/laudos/novo/page.tsx b/app/doctor/medicos/[id]/laudos/novo/page.tsx index 9c9b8a9..1bac56b 100644 --- a/app/doctor/medicos/[id]/laudos/novo/page.tsx +++ b/app/doctor/medicos/[id]/laudos/novo/page.tsx @@ -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() {
-
- - -
diff --git a/app/doctor/medicos/page.tsx b/app/doctor/medicos/page.tsx index f3f3c1e..7973a65 100644 --- a/app/doctor/medicos/page.tsx +++ b/app/doctor/medicos/page.tsx @@ -8,7 +8,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 { @@ -283,9 +283,10 @@ export default function PacientesPage() { - + - + Laudos - - alert(`Agenda para paciente ID: ${p.id}`) - } - > - - Ver agenda - { const newPacientes = pacientes.filter( diff --git a/app/manager/home/page.tsx b/app/manager/home/page.tsx index 614df35..720398e 100644 --- a/app/manager/home/page.tsx +++ b/app/manager/home/page.tsx @@ -6,7 +6,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 { Edit, Trash2, Eye, Calendar, Loader2 } from "lucide-react"; +import { Edit, Trash2, Eye, Calendar, Loader2, MoreVertical } from "lucide-react"; import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle } from "@/components/ui/alert-dialog"; // Imports dos Serviços @@ -326,7 +326,9 @@ export default function DoctorsPage() { -
Ações
+
+ +
openDetailsDialog(doctor)}> diff --git a/app/manager/pacientes/page.tsx b/app/manager/pacientes/page.tsx index ad6c58b..e6886f3 100644 --- a/app/manager/pacientes/page.tsx +++ b/app/manager/pacientes/page.tsx @@ -3,30 +3,10 @@ import { useState, useEffect, useCallback } from "react"; 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 { Edit, Trash2, Eye, Calendar, Filter, Loader2 } from "lucide-react"; -import { - AlertDialog, - AlertDialogAction, - AlertDialogCancel, - AlertDialogContent, - AlertDialogDescription, - AlertDialogFooter, - AlertDialogHeader, - AlertDialogTitle, -} from "@/components/ui/alert-dialog"; +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, 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 Sidebar from "@/components/Sidebar"; @@ -291,107 +271,39 @@ export default function PacientesPage() { -
Ações
+
+ +
openDetailsDialog(String(patient.id))}> Ver detalhes + + + + Editar + + - - - - Editar - - - - - - Marcar consulta - - - openDeleteDialog(String(patient.id)) - } - > - - Excluir - - -
- - - )) - )} - - - )} -
-
- - {/* --- SEÇÃO DE CARDS (VISÍVEL APENAS EM TELAS MENORES QUE MD) --- */} - {/* Garantir que os cards apareçam em telas menores e se escondam em MD+ */} -
- {error ? ( -
{`Erro ao carregar pacientes: ${error}`}
- ) : loading ? ( -
- Carregando pacientes... -
- ) : filteredPatients.length === 0 ? ( -
- {allPatients.length === 0 ? "Nenhum paciente cadastrado" : "Nenhum paciente encontrado com os filtros aplicados"} -
- ) : ( -
- {currentPatients.map((patient) => ( -
-
-
- {patient.nome} - {patient.vip && ( - VIP - )} -
-
Telefone: {patient.telefone}
-
Convênio: {patient.convenio}
-
- - -
-
- - openDetailsDialog(String(patient.id))}> - - Ver detalhes - - - - - - Editar - - - - - - Marcar consulta - - openDeleteDialog(String(patient.id))}> - - Excluir - - -
-
- ))} -
- )} + + + Marcar consulta + + openDeleteDialog(String(patient.id))}> + + Excluir + + + + + + )))} + + + )} +
{/* Paginação */} diff --git a/app/secretary/appointments/page.tsx b/app/secretary/appointments/page.tsx index acd1c87..e1702e2 100644 --- a/app/secretary/appointments/page.tsx +++ b/app/secretary/appointments/page.tsx @@ -283,33 +283,24 @@ export default function SecretaryAppointments() {
-
- - -
- - - )) - ) : ( -

Nenhuma consulta encontrada.

- )} - - +
+ + +
+ + + )) + ) : ( +

Nenhuma consulta encontrada.

+ )} + + {/* MODAL DE EDIÇÃO */} diff --git a/app/secretary/pacientes/page.tsx b/app/secretary/pacientes/page.tsx index fceeab4..4a68031 100644 --- a/app/secretary/pacientes/page.tsx +++ b/app/secretary/pacientes/page.tsx @@ -4,38 +4,11 @@ import { useState, useEffect, useCallback } from "react"; 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 { - AlertDialog, - AlertDialogAction, - AlertDialogCancel, - AlertDialogContent, - AlertDialogDescription, - AlertDialogFooter, - AlertDialogHeader, - AlertDialogTitle, -} from "@/components/ui/alert-dialog"; +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, 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"; import Sidebar from "@/components/Sidebar"; @@ -43,58 +16,59 @@ import Sidebar from "@/components/Sidebar"; 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"); + // --- 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([]); - // Lista após a aplicação dos filtros (base para a paginação) - const [filteredPatients, setFilteredPatients] = useState([]); + // Lista completa, carregada da API uma única vez + const [allPatients, setAllPatients] = useState([]); + // Lista após a aplicação dos filtros (base para a paginação) + const [filteredPatients, setFilteredPatients] = useState([]); - const [loading, setLoading] = useState(true); - const [error, setError] = useState(null); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); - // --- ESTADOS DE PAGINAÇÃO --- - const [page, setPage] = useState(1); + // --- 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); + // 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); - // --- ESTADOS DE DIALOGS --- - const [deleteDialogOpen, setDeleteDialogOpen] = useState(false); - const [patientToDelete, setPatientToDelete] = useState(null); - const [detailsDialogOpen, setDetailsDialogOpen] = useState(false); - const [patientDetails, setPatientDetails] = useState(null); + // --- ESTADOS DE DIALOGS --- + const [deleteDialogOpen, setDeleteDialogOpen] = useState(false); + const [patientToDelete, setPatientToDelete] = useState(null); + const [detailsDialogOpen, setDetailsDialogOpen] = useState(false); + const [patientDetails, setPatientDetails] = useState(null); - // --- FUNÇÕES DE LÓGICA --- + // --- FUNÇÕES DE LÓGICA --- - // 1. Função para carregar TODOS os pacientes da API - const fetchAllPacientes = useCallback(async () => { - setLoading(true); - setError(null); - try { - // Como o backend retorna um array, chamamos sem paginação - const res = await patientsService.list(); + // 1. Função para carregar TODOS os pacientes da API + const fetchAllPacientes = useCallback( + async () => { + setLoading(true); + setError(null); + 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 ?? "—", - telefone: p.phone_mobile ?? p.phone1 ?? "—", - cidade: p.city ?? "—", - estado: p.state ?? "—", - // Formate as datas se necessário, aqui usamos como string - 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 - status: p.status ?? undefined, - })); + const mapped = res.map((p: any) => ({ + id: String(p.id ?? ""), + nome: p.full_name ?? "—", + telefone: p.phone_mobile ?? p.phone1 ?? "—", + cidade: p.city ?? "—", + estado: p.state ?? "—", + // Formate as datas se necessário, aqui usamos como string + 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 + status: p.status ?? undefined, + })); setAllPatients(mapped); } catch (e: any) { @@ -105,31 +79,32 @@ export default function PacientesPage() { } }, []); - // 2. Efeito para aplicar filtros e calcular a lista filtrada (chama-se quando allPatients ou filtros mudam) - useEffect(() => { - const filtered = allPatients.filter((patient) => { - // Filtro por termo de busca (Nome ou Telefone) - const matchesSearch = - patient.nome?.toLowerCase().includes(searchTerm.toLowerCase()) || - patient.telefone?.includes(searchTerm); + // 2. Efeito para aplicar filtros e calcular a lista filtrada (chama-se quando allPatients ou filtros mudam) + useEffect(() => { + const filtered = allPatients.filter((patient) => { + // Filtro por termo de busca (Nome ou Telefone) + const matchesSearch = + patient.nome?.toLowerCase().includes(searchTerm.toLowerCase()) || + patient.telefone?.includes(searchTerm); - // Filtro por Convênio - const matchesConvenio = - convenioFilter === "all" || patient.convenio === convenioFilter; + // Filtro por Convênio + const matchesConvenio = + convenioFilter === "all" || + patient.convenio === convenioFilter; - // Filtro por VIP - const matchesVip = - vipFilter === "all" || - (vipFilter === "vip" && patient.vip) || - (vipFilter === "regular" && !patient.vip); + // Filtro por VIP + const matchesVip = + vipFilter === "all" || + (vipFilter === "vip" && patient.vip) || + (vipFilter === "regular" && !patient.vip); - return matchesSearch && matchesConvenio && matchesVip; - }); + return matchesSearch && matchesConvenio && matchesVip; + }); - setFilteredPatients(filtered); - // Garante que a página atual seja válida após a filtragem - setPage(1); - }, [allPatients, searchTerm, convenioFilter, vipFilter]); + setFilteredPatients(filtered); + // Garante que a página atual seja válida após a filtragem + setPage(1); + }, [allPatients, searchTerm, convenioFilter, vipFilter]); // 3. Efeito inicial para buscar os pacientes useEffect(() => { @@ -137,18 +112,18 @@ export default function PacientesPage() { // eslint-disable-next-line react-hooks/exhaustive-deps }, []); - // --- LÓGICA DE AÇÕES (DELETAR / VER DETALHES) --- + // --- LÓGICA DE AÇÕES (DELETAR / VER DETALHES) --- - const openDetailsDialog = async (patientId: string) => { - setDetailsDialogOpen(true); - setPatientDetails(null); - try { - const res = await patientsService.getById(patientId); - setPatientDetails(Array.isArray(res) ? res[0] : res); // Supondo que retorne um array com um item - } catch (e: any) { - setPatientDetails({ error: e?.message || "Erro ao buscar detalhes" }); - } - }; + const openDetailsDialog = async (patientId: string) => { + setDetailsDialogOpen(true); + setPatientDetails(null); + try { + const res = await patientsService.getById(patientId); + setPatientDetails(Array.isArray(res) ? res[0] : res); // Supondo que retorne um array com um item + } catch (e: any) { + setPatientDetails({ error: e?.message || "Erro ao buscar detalhes" }); + } + }; const handleDeletePatient = async (patientId: string) => { try { @@ -192,19 +167,18 @@ export default function PacientesPage() { - {/* Bloco de Filtros (Responsividade APLICADA) */} -
- + {/* Bloco de Filtros (Responsividade APLICADA) */} +
+ - {/* Busca - Ocupa 100% no mobile, depois cresce */} - setSearchTerm(e.target.value)} - // w-full no mobile, depois flex-grow para ocupar o espaço disponível - className="w-full sm:flex-grow sm:max-w-[300px] p-2 border rounded-md text-sm" - /> + {/* Busca - Ocupa 100% no mobile, depois cresce */} + setSearchTerm(e.target.value)} + className="w-full sm:flex-grow sm:min-w-[150px] p-2 border rounded-md text-sm" + /> {/* Convênio - Ocupa a largura total em telas pequenas, depois se ajusta */}
diff --git a/components/ui/button.tsx b/components/ui/button.tsx index a45c066..14b6319 100644 --- a/components/ui/button.tsx +++ b/components/ui/button.tsx @@ -41,8 +41,10 @@ export interface ButtonProps const Button = React.forwardRef( ({ className, variant, size, asChild = false, ...props }, ref) => { const Comp = asChild ? Slot : 'button' + return (