"use client"; import { Card, CardContent, CardDescription, CardHeader, CardTitle, } from "@/components/ui/card"; import { Button } from "@/components/ui/button"; import { Calendar, Clock, User, Trash2 } from "lucide-react"; import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, } from "@/components/ui/alert-dialog"; import Link from "next/link"; import { useEffect, useState } from "react"; import { toast } from "@/hooks/use-toast"; import { useAuthLayout } from "@/hooks/useAuthLayout"; import { patientsService } from "@/services/patientsApi.mjs"; import { appointmentsService } from "@/services/appointmentsApi.mjs"; import { format, parseISO, isAfter, isSameMonth, startOfToday } from "date-fns"; import { ptBR } from "date-fns/locale"; import { AvailabilityService } from "@/services/availabilityApi.mjs"; import { exceptionsService } from "@/services/exceptionApi.mjs"; import { doctorsService } from "@/services/doctorsApi.mjs"; import { usersService } from "@/services/usersApi.mjs"; import Sidebar from "@/components/Sidebar"; import WeeklyScheduleCard from "@/components/ui/WeeklyScheduleCard"; // --- TIPOS ADICIONADOS PARA CORREÇÃO --- type Appointment = { id: string; doctor_id: string; patient_id: string; scheduled_at: string; status: string; }; type EnrichedAppointment = Appointment & { patientName: string; }; // --- FIM DOS TIPOS ADICIONADOS --- type Availability = { id: string; doctor_id: string; weekday: string; start_time: string; end_time: string; slot_minutes: number; appointment_type: string; active: boolean; created_at: string; updated_at: string; created_by: string; updated_by: string | null; }; type Schedule = { weekday: object; }; type Doctor = { id: string; user_id: string | null; crm: string; crm_uf: string; specialty: string; full_name: string; cpf: string; email: string; phone_mobile: string | null; phone2: string | null; cep: string | null; street: string | null; number: string | null; complement: string | null; neighborhood: string | null; city: string | null; state: string | null; birth_date: string | null; rg: string | null; active: boolean; created_at: string; updated_at: string; created_by: string; updated_by: string | null; max_days_in_advance: number; rating: number | null; }; interface UserPermissions { isAdmin: boolean; isManager: boolean; isDoctor: boolean; isSecretary: boolean; isAdminOrManager: boolean; } interface UserData { user: { id: string; email: string; email_confirmed_at: string | null; created_at: string | null; last_sign_in_at: string | null; }; profile: { id: string; full_name: string; email: string; phone: string; avatar_url: string | null; disabled: boolean; created_at: string | null; updated_at: string | null; }; roles: string[]; permissions: UserPermissions; } interface Exception { id: string; doctor_id: string; date: string; start_time: string | null; end_time: string | null; kind: "bloqueio" | "disponibilidade"; reason: string | null; created_at: string; created_by: string; } // Minimal type for Patient, adjust if more fields are needed type Patient = { id: string; full_name: string; }; export default function PatientDashboard() { const { user } = useAuthLayout({ requiredRole: ['medico'] }); const [loggedDoctor, setLoggedDoctor] = useState(null); const [userData, setUserData] = useState(); const [availability, setAvailability] = useState(null); const [exceptions, setExceptions] = useState([]); const [schedule, setSchedule] = useState>({}); const formatTime = (time?: string | null) => time?.split(":")?.slice(0, 2).join(":") ?? ""; const [deleteDialogOpen, setDeleteDialogOpen] = useState(false); const [exceptionToDelete, setExceptionToDelete] = useState(null); const [error, setError] = useState(null); const [nextAppointment, setNextAppointment] = useState(null); const [monthlyCount, setMonthlyCount] = useState(0); const weekdaysPT: Record = { sunday: "Domingo", monday: "Segunda", tuesday: "Terça", wednesday: "Quarta", thursday: "Quinta", friday: "Sexta", saturday: "Sábado" }; useEffect(() => { const fetchData = async () => { if (!user?.id) return; try { const doctorsList: Doctor[] = await doctorsService.list(); const currentDoctor = doctorsList.find(doc => doc.user_id === user.id); if (!currentDoctor) { setError("Perfil de médico não encontrado para este usuário."); return; } setLoggedDoctor(currentDoctor); const [appointmentsList, patientsList, availabilityList, exceptionsList] = await Promise.all([ appointmentsService.list(), patientsService.list(), AvailabilityService.list(), exceptionsService.list() ]); const patientsMap = new Map(patientsList.map((p: Patient) => [p.id, p.full_name])); const doctorAppointments = appointmentsList .filter((apt: Appointment) => apt.doctor_id === currentDoctor.id) .map((apt: Appointment): EnrichedAppointment => ({ ...apt, patientName: String(patientsMap.get(apt.patient_id) || "Paciente Desconhecido"), })); const today = startOfToday(); const upcomingAppointments = doctorAppointments .filter(apt => isAfter(parseISO(apt.scheduled_at), today)) .sort((a, b) => new Date(a.scheduled_at).getTime() - new Date(b.scheduled_at).getTime()); setNextAppointment(upcomingAppointments[0] || null); const activeStatuses = ['confirmed', 'requested', 'checked_in']; const currentMonthAppointments = doctorAppointments.filter(apt => isSameMonth(parseISO(apt.scheduled_at), new Date()) && activeStatuses.includes(apt.status) ); setMonthlyCount(currentMonthAppointments.length); setAvailability(availabilityList.filter((d: any) => d.doctor_id === currentDoctor.id)); setExceptions(exceptionsList.filter((e: any) => e.doctor_id === currentDoctor.id)); } catch (e: any) { setError(e?.message || "Erro ao buscar dados do dashboard"); console.error("Erro no dashboard:", e); } }; fetchData(); }, [user]); function findDoctorById(id: string, doctors: Doctor[]) { return doctors.find((doctor) => doctor.user_id === id); } const openDeleteDialog = (exceptionId: string) => { setExceptionToDelete(exceptionId); setDeleteDialogOpen(true); }; const handleDeleteException = async (ExceptionId: string) => { try { const res = await exceptionsService.delete(ExceptionId); if (res && res.error) { throw new Error(res.message || "A API retornou um erro"); } toast({ title: "Sucesso", description: "Exceção deletada com sucesso" }); setExceptions((prev: Exception[]) => prev.filter((p) => String(p.id) !== String(ExceptionId))); } catch (e: any) { toast({ title: "Erro", description: e?.message || "Não foi possível deletar a exceção" }); } setDeleteDialogOpen(false); setExceptionToDelete(null); }; function formatAvailability(data: Availability[]) { if (!data) return {}; const schedule = data.reduce((acc: any, item) => { const { weekday, start_time, end_time } = item; if (!acc[weekday]) acc[weekday] = []; acc[weekday].push({ start: start_time, end: end_time }); return acc; }, {} as Record); return schedule; } useEffect(() => { if (availability) { const formatted = formatAvailability(availability); setSchedule(formatted); } }, [availability]); return (

Dashboard

Bem-vindo ao seu portal de consultas médicas

Próxima Consulta {nextAppointment ? ( <>

{nextAppointment.patientName} - {format(parseISO(nextAppointment.scheduled_at), "HH:mm")}

{format(parseISO(nextAppointment.scheduled_at), "dd MMM", { locale: ptBR })}
) : ( <>
Nenhuma

Sem próximas consultas

)}
Consultas Este Mês
{monthlyCount}

{monthlyCount === 1 ? '1 agendada' : `${monthlyCount} agendadas`}

Perfil
100%

Dados completos

Ações Rápidas Acesse rapidamente as principais funcionalidades
Horário Semanal Confira rapidamente a sua disponibilidade da semana {loggedDoctor && }
Exceções Bloqueios e liberações eventuais de agenda {exceptions && exceptions.length > 0 ? ( exceptions.map((ex: Exception) => { const date = new Date(ex.date).toLocaleDateString("pt-BR", { weekday: "long", day: "2-digit", month: "long", timeZone: "UTC" }); const startTime = formatTime(ex.start_time); const endTime = formatTime(ex.end_time); return (

{date}

{startTime && endTime ? `${startTime} - ${endTime}` : "Dia todo"}

{ex.kind === "bloqueio" ? "Bloqueio" : "Liberação"}

{ex.reason || "Sem motivo especificado"}

); }) ) : (

Nenhuma exceção registrada.

)}
Confirmar exclusão Tem certeza que deseja excluir esta exceção? Esta ação não pode ser desfeita. Cancelar exceptionToDelete && handleDeleteException(exceptionToDelete)} className="bg-red-600 hover:bg-red-700"> Excluir
); }