diff --git a/app/patient/appointments/page.tsx b/app/patient/appointments/page.tsx index fbed4fc..d8ff972 100644 --- a/app/patient/appointments/page.tsx +++ b/app/patient/appointments/page.tsx @@ -1,93 +1,83 @@ "use client"; import { useState, useEffect } from "react"; -import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/ui/card"; +import { + Card, + CardContent, + CardDescription, + CardHeader, + CardTitle, +} from "@/components/ui/card"; import { Button } from "@/components/ui/button"; import { Badge } from "@/components/ui/badge"; -import { Calendar, Clock, CalendarDays, X } from "lucide-react"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, +} from "@/components/ui/dialog"; +import { + Calendar, + Clock, + MapPin, + Phone, + User, + X, + AlertCircle, +} from "lucide-react"; import { toast } from "sonner"; - import { appointmentsService } from "@/services/appointmentsApi.mjs"; import { usersService } from "@/services/usersApi.mjs"; +import { doctorsService } from "@/services/doctorsApi.mjs"; import Sidebar from "@/components/Sidebar"; -// Tipagem correta para o usuário -interface UserProfile { - id: string; - full_name: string; - email: string; - phone?: string; - avatar_url?: string; -} - -interface User { - user: { - id: string; - email: string; - }; - profile: UserProfile; - roles: string[]; - permissions?: any; -} - -interface Appointment { - id: string; - doctor_id: string; - scheduled_at: string; - status: string; - doctorName?: string; -} - export default function PatientAppointmentsPage() { - const [appointments, setAppointments] = useState([]); + const [appointments, setAppointments] = useState([]); const [isLoading, setIsLoading] = useState(true); - const [userData, setUserData] = useState(null); + + // Estados para cancelamento + const [cancelModal, setCancelModal] = useState(false); + const [selectedAppointment, setSelectedAppointment] = useState(null); - // --- Busca o usuário logado --- - const fetchUser = async () => { - try { - const user: User = await usersService.getMe(); - if (!user.roles.includes("patient") && !user.roles.includes("user")) { - toast.error("Apenas pacientes podem visualizar suas consultas."); - setIsLoading(false); - return null; - } - setUserData(user); - return user; - } catch (err) { - console.error("Erro ao buscar usuário logado:", err); - toast.error("Não foi possível identificar o usuário logado."); - setIsLoading(false); - return null; - } - }; - - // --- Busca consultas do paciente --- - const fetchAppointments = async (patientId: string) => { + const fetchData = async () => { setIsLoading(true); try { - const queryParams = `patient_id=eq.${patientId}&order=scheduled_at.desc`; - const appointmentsList: Appointment[] = await appointmentsService.search_appointment(queryParams); + // 1. Obter usuário logado + const user = await usersService.getMe(); + if (!user || !user.user?.id) { + toast.error("Usuário não identificado."); + return; + } - // Buscar nome do médico para cada consulta - const appointmentsWithDoctor = await Promise.all( - appointmentsList.map(async (apt) => { - let doctorName = apt.doctor_id; - if (apt.doctor_id) { - try { - const doctorInfo = await usersService.full_data(apt.doctor_id); - doctorName = doctorInfo?.profile?.full_name || apt.doctor_id; - } catch (err) { - console.error("Erro ao buscar nome do médico:", err); - } - } - return { ...apt, doctorName }; - }) - ); + // 2. Buscar médicos e agendamentos em paralelo + // Filtra apenas agendamentos deste paciente + const queryParams = `patient_id=eq.${"user.user.id"}&order=scheduled_at.desc`; + console.log("id do paciente:", user.profile.id); + const [appointmentList, doctorList] = await Promise.all([ + appointmentsService.search_appointment(queryParams), + doctorsService.list(), + ]); + console.log("Agendamentos obtidos:", appointmentList); + console.log("Médicos obtidos:", doctorList); + // 3. Mapear médicos para acesso rápido + const doctorMap = new Map(doctorList.map((d: any) => [d.id, d])); - setAppointments(appointmentsWithDoctor); - } catch (err) { - console.error("Erro ao carregar consultas:", err); + // 4. Enriquecer os agendamentos com dados do médico + const enrichedAppointments = appointmentList.map((apt: any) => ({ + ...apt, + doctor: doctorMap.get(apt.doctor_id) || { + full_name: "Médico não encontrado", + specialty: "Clínico Geral", + location: "Consultório", + phone: "N/A" + }, + })); + console.log("Agendamentos enriquecidos:", enrichedAppointments); + setAppointments(enrichedAppointments); + } catch (error) { + console.error("Erro ao buscar dados:", error); toast.error("Não foi possível carregar suas consultas."); } finally { setIsLoading(false); @@ -95,96 +85,187 @@ export default function PatientAppointmentsPage() { }; useEffect(() => { - (async () => { - const user = await fetchUser(); - if (user?.user.id) { - await fetchAppointments(user.user.id); - } - })(); + fetchData(); }, []); - const getStatusBadge = (status: string) => { - switch (status) { - case "requested": - return Solicitada; - case "confirmed": - return Confirmada; - case "checked_in": - return Check-in; - case "completed": - return Realizada; - case "cancelled": - return Cancelada; - default: - return {status}; + // --- LÓGICA DE CANCELAMENTO --- + const handleCancelClick = (appointment: any) => { + setSelectedAppointment(appointment); + setCancelModal(true); + }; + + const confirmCancel = async () => { + if (!selectedAppointment) return; + try { + // Opção A: Deletar o registro (como no código da secretária) + await appointmentsService.delete(selectedAppointment.id); + + // Opção B: Se preferir apenas mudar o status, descomente abaixo e comente a linha acima: + // await appointmentsService.update(selectedAppointment.id, { status: 'cancelled' }); + + setAppointments((prev) => + prev.filter((apt) => apt.id !== selectedAppointment.id) + ); + setCancelModal(false); + toast.success("Consulta cancelada com sucesso."); + } catch (error) { + console.error("Erro ao cancelar consulta:", error); + toast.error("Não foi possível cancelar a consulta."); } }; - const handleReschedule = (apt: Appointment) => { - toast.info(`Funcionalidade de reagendamento da consulta ${apt.id} ainda não implementada`); - }; - - const handleCancel = (apt: Appointment) => { - toast.info(`Funcionalidade de cancelamento da consulta ${apt.id} ainda não implementada`); - }; - - return ( - -
-
-
-

Minhas Consultas

-

Veja, reagende ou cancele suas consultas

-
-
+ return ( + +
+
+
+

Minhas Consultas

+

+ Acompanhe seu histórico e próximos agendamentos +

+
+
{isLoading ? (

Carregando consultas...

- ) : appointments.length === 0 ? ( -

Você ainda não possui consultas agendadas.

- ) : ( - appointments.map((apt) => ( - - -
- {apt.doctorName} - Especialidade: N/A + ) : appointments.length > 0 ? ( + appointments.map((appointment) => ( + + +
+
+ + {appointment.doctor.full_name} + + + {appointment.doctor.specialty} + +
+ {getStatusBadge(appointment.status)}
- {getStatusBadge(apt.status)}
- -
-
- - {new Date(apt.scheduled_at).toLocaleDateString("pt-BR")} + +
+ {/* Coluna 1: Data e Hora */} +
+
+ + Dr(a). {appointment.doctor.full_name.split(' ')[0]} +
+
+ + {new Date(appointment.scheduled_at).toLocaleDateString( + "pt-BR", + { timeZone: "UTC" } + )} +
+
+ + {new Date(appointment.scheduled_at).toLocaleTimeString( + "pt-BR", + { + hour: "2-digit", + minute: "2-digit", + timeZone: "UTC", + } + )} +
-
- - {new Date(apt.scheduled_at).toLocaleTimeString("pt-BR", { - hour: "2-digit", - minute: "2-digit", - })} + + {/* Coluna 2: Localização e Contato */} +
+
+ + {appointment.doctor.location || "Local a definir"} +
+
+ + {appointment.doctor.phone || "Contato não disponível"} +
-
- {apt.status !== "cancelled" && ( - <> - - - - )} -
+ + {/* Ações */} + {["requested", "confirmed"].includes(appointment.status) && ( +
+ +
+ )} )) + ) : ( +
+ +

Você ainda não possui consultas agendadas.

+
)}
+ + {/* Modal de Confirmação de Cancelamento */} + + + + + + Cancelar Consulta + + + Tem certeza que deseja cancelar sua consulta com{" "} + {selectedAppointment?.doctor?.full_name} no dia{" "} + {selectedAppointment && + new Date(selectedAppointment.scheduled_at).toLocaleDateString( + "pt-BR", { timeZone: "UTC" } + )} + ? Esta ação não pode ser desfeita. + + + + + + + + ); } + +// Helper para Badges (Mantido consistente com o código da secretária) +const getStatusBadge = (status: string) => { + switch (status) { + case "requested": + return ( + Solicitada + ); + case "confirmed": + return Confirmada; + case "checked_in": + return ( + Check-in + ); + case "completed": + return Realizada; + case "cancelled": + return Cancelada; + case "no_show": + return ( + Não Compareceu + ); + default: + return {status}; + } +}; \ No newline at end of file