"use client"; import { useState, useEffect, useMemo } from "react"; import { Card, CardContent, CardDescription, CardHeader, CardTitle, } from "@/components/ui/card"; import { Button } from "@/components/ui/button"; import { Badge } from "@/components/ui/badge"; import { Dialog } from "@/components/ui/dialog"; import { Input } from "@/components/ui/input"; // Importei o Input import { Calendar as CalendarShadcn } from "@/components/ui/calendar"; import { Separator } from "@/components/ui/separator"; import { Calendar as CalendarIcon, Clock, MapPin, Phone, User, Trash2, Pencil, List, RefreshCw, Loader2, Search, // Importei o ícone de busca } from "lucide-react"; import { format, parseISO, isValid, isToday, isTomorrow } from "date-fns"; import { ptBR } from "date-fns/locale"; import { toast } from "sonner"; import Link from "next/link"; import { appointmentsService } from "@/services/appointmentsApi.mjs"; import { patientsService } from "@/services/patientsApi.mjs"; import { doctorsService } from "@/services/doctorsApi.mjs"; import Sidebar from "@/components/Sidebar"; export default function SecretaryAppointments() { const [appointments, setAppointments] = useState([]); const [isLoading, setIsLoading] = useState(true); const [selectedAppointment, setSelectedAppointment] = useState(null); // Estados dos Modais const [deleteModal, setDeleteModal] = useState(false); const [editModal, setEditModal] = useState(false); // Estado da Busca const [searchTerm, setSearchTerm] = useState(""); // Estado para o formulário de edição const [editFormData, setEditFormData] = useState({ date: "", time: "", status: "", }); // Estado de data selecionada const [selectedDate, setSelectedDate] = useState(new Date()); const fetchData = async () => { setIsLoading(true); try { const queryParams = "order=scheduled_at.asc"; const [appointmentList, patientList, doctorList] = await Promise.all([ appointmentsService.search_appointment(queryParams), patientsService.list(), doctorsService.list(), ]); const patientMap = new Map(patientList.map((p: any) => [p.id, p])); const doctorMap = new Map(doctorList.map((d: any) => [d.id, d])); const enrichedAppointments = appointmentList.map((apt: any) => ({ ...apt, patient: patientMap.get(apt.patient_id) || { full_name: "Paciente não encontrado", }, doctor: doctorMap.get(apt.doctor_id) || { full_name: "Médico não encontrado", specialty: "N/A", }, })); setAppointments(enrichedAppointments); } catch (error) { console.error("Falha ao buscar agendamentos:", error); toast.error("Não foi possível carregar a lista de agendamentos."); } finally { setIsLoading(false); } }; useEffect(() => { fetchData(); }, []); // --- Filtragem e Agrupamento --- const groupedAppointments = useMemo(() => { let filteredList = appointments; // 1. Filtro de Texto (Nome do Paciente ou Médico) if (searchTerm) { const lowerTerm = searchTerm.toLowerCase(); filteredList = filteredList.filter( (apt) => apt.patient.full_name.toLowerCase().includes(lowerTerm) || apt.doctor.full_name.toLowerCase().includes(lowerTerm) ); } // 2. Filtro de Data (se selecionada) if (selectedDate) { filteredList = filteredList.filter((apt) => { if (!apt.scheduled_at) return false; const iso = apt.scheduled_at.toString(); return iso.startsWith(format(selectedDate, "yyyy-MM-dd")); }); } // 3. Agrupamento por dia return filteredList.reduce((acc: Record, apt: any) => { if (!apt.scheduled_at) return acc; const dateObj = new Date(apt.scheduled_at); if (!isValid(dateObj)) return acc; const key = format(dateObj, "yyyy-MM-dd"); if (!acc[key]) acc[key] = []; acc[key].push(apt); return acc; }, {}); }, [appointments, selectedDate, searchTerm]); // Dias que têm consulta (para destacar no calendário) const bookedDays = useMemo( () => appointments .map((apt) => apt.scheduled_at ? new Date(apt.scheduled_at) : null ) .filter((d): d is Date => d !== null && isValid(d)), [appointments] ); const formatDisplayDate = (dateString: string) => { const date = parseISO(dateString); if (isToday(date)) { return `Hoje, ${format(date, "dd 'de' MMMM", { locale: ptBR })}`; } if (isTomorrow(date)) { return `Amanhã, ${format(date, "dd 'de' MMMM", { locale: ptBR })}`; } return format(date, "EEEE, dd 'de' MMMM", { locale: ptBR }); }; // --- LÓGICA DE EDIÇÃO E DELEÇÃO --- const handleEdit = (appointment: any) => { setSelectedAppointment(appointment); const appointmentDate = new Date(appointment.scheduled_at); setEditFormData({ date: appointmentDate.toISOString().split("T")[0], time: appointmentDate.toLocaleTimeString("pt-BR", { hour: "2-digit", minute: "2-digit", timeZone: "UTC", }), status: appointment.status, }); setEditModal(true); }; const confirmEdit = async () => { if ( !selectedAppointment || !editFormData.date || !editFormData.time || !editFormData.status ) { toast.error("Todos os campos são obrigatórios para a edição."); return; } try { const newScheduledAt = new Date( `${editFormData.date}T${editFormData.time}:00Z` ).toISOString(); const updatePayload = { scheduled_at: newScheduledAt, status: editFormData.status, }; await appointmentsService.update(selectedAppointment.id, updatePayload); await fetchData(); setEditModal(false); toast.success("Consulta atualizada com sucesso!"); } catch (error) { console.error("Erro ao atualizar consulta:", error); toast.error("Não foi possível atualizar a consulta."); } }; const handleDelete = (appointment: any) => { setSelectedAppointment(appointment); setDeleteModal(true); }; const confirmDelete = async () => { if (!selectedAppointment) return; try { await appointmentsService.delete(selectedAppointment.id); setAppointments((prev) => prev.filter((apt) => apt.id !== selectedAppointment.id) ); setDeleteModal(false); toast.success("Consulta deletada com sucesso!"); } catch (error) { console.error("Erro ao deletar consulta:", error); toast.error("Não foi possível deletar a consulta."); } }; return (
{/* Cabeçalho principal */}

Agenda Médica

Consultas para os pacientes

{/* Barra de Filtros e Ações */}

{selectedDate ? `Agenda de ${format(selectedDate, "dd/MM/yyyy")}` : "Todas as Consultas"}

{/* BARRA DE PESQUISA ADICIONADA AQUI */}
setSearchTerm(e.target.value)} />
{/* Grid com calendário + lista */}
{/* Coluna esquerda: calendário */}
Filtrar por Data Selecione um dia para ver os detalhes.
{/* Coluna direita: lista de consultas */}
{isLoading ? (
) : Object.keys(groupedAppointments).length === 0 ? ( Nenhuma consulta encontrada

{searchTerm ? "Nenhum resultado para a busca." : selectedDate ? "Não há agendamentos para esta data." : "Não há consultas agendadas."}

) : ( Object.entries(groupedAppointments).map( ([date, appointmentsForDay]) => (

{formatDisplayDate(date)}

{appointmentsForDay.map((appointment: any) => { const scheduledAtDate = new Date( appointment.scheduled_at ); return ( {/* Coluna 1: Paciente + hora */}
{appointment.patient.full_name}
{isValid(scheduledAtDate) ? format(scheduledAtDate, "HH:mm") : "--:--"}
{/* Coluna 2: Médico / local / telefone */}
{appointment.doctor.full_name}
{appointment.doctor.location || "Local a definir"}
{appointment.doctor.phone || "N/A"}
{getStatusBadge(appointment.status)}
{/* Coluna 3: Ações */}
); })}
) ) )}
{/* MODAL DE EDIÇÃO */} {/* Modal de edição permanece o mesmo, adicione o DialogContent se precisar */} {/* Aqui estou assumindo que você tem o conteúdo do Dialog no seu código original ou em outro lugar, pois ele não estava completo no snippet anterior */} {/* Modal de Deleção */} {/* Modal de deleção permanece o mesmo */}
); } 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}; } };