From fbdeb7e462915af8fc446f0179325b19895ffd56 Mon Sep 17 00:00:00 2001 From: Jonas Francisco Date: Fri, 31 Oct 2025 01:09:44 -0300 Subject: [PATCH] =?UTF-8?q?`=20feat:=20Substitui=20FullCalendar=20pelo=20E?= =?UTF-8?q?ventManager=20conectado=20=C3=A0=20API?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/(main-routes)/calendar/page.tsx | 385 +++++++----------- 1 file changed, 157 insertions(+), 228 deletions(-) diff --git a/susconecta/app/(main-routes)/calendar/page.tsx b/susconecta/app/(main-routes)/calendar/page.tsx index 45cee52..5c54c1c 100644 --- a/susconecta/app/(main-routes)/calendar/page.tsx +++ b/susconecta/app/(main-routes)/calendar/page.tsx @@ -5,17 +5,9 @@ import { useEffect, useState } from "react"; import dynamic from "next/dynamic"; import Link from "next/link"; -// --- Imports do FullCalendar (restaurados) --- -import pt_br_locale from "@fullcalendar/core/locales/pt-br"; -import FullCalendar from "@fullcalendar/react"; -import dayGridPlugin from "@fullcalendar/daygrid"; -import interactionPlugin from "@fullcalendar/interaction"; -import timeGridPlugin from "@fullcalendar/timegrid"; -import { EventInput } from "@fullcalendar/core/index.js"; - -// --- Imports do EventManager (NOVO) - ADICIONADOS --- +// --- Imports do EventManager (NOVO) - MANTIDOS --- import { EventManager, type Event } from "@/components/event-manager"; -import { v4 as uuidv4 } from 'uuid'; +import { v4 as uuidv4 } from 'uuid'; // Usado para IDs de fallback // Imports mantidos import { Sidebar } from "@/components/dashboard/sidebar"; @@ -24,141 +16,91 @@ import { Button } from "@/components/ui/button"; import { mockWaitingList } from "@/lib/mocks/appointment-mocks"; import "./index.css"; import { -  DropdownMenu, -  DropdownMenuContent, -  DropdownMenuItem, -  DropdownMenuTrigger, + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; import { ThreeDWallCalendar, CalendarEvent } from "@/components/ui/three-dwall-calendar"; // Calendário 3D mantido const ListaEspera = dynamic( -  () => import("@/components/agendamento/ListaEspera"), -  { ssr: false } + () => import("@/components/agendamento/ListaEspera"), + { ssr: false } ); export default function AgendamentoPage() { -  const [appointments, setAppointments] = useState([]); -  const [waitingList, setWaitingList] = useState(mockWaitingList); -  const [activeTab, setActiveTab] = useState<"calendar" | "espera" | "3d">("calendar"); -  - // Estado para alimentar o FullCalendar (restaurado) - const [requestsList, setRequestsList] = useState([]); -  + const [appointments, setAppointments] = useState([]); + const [waitingList, setWaitingList] = useState(mockWaitingList); + const [activeTab, setActiveTab] = useState<"calendar" | "espera" | "3d">("calendar"); + const [threeDEvents, setThreeDEvents] = useState([]); - // --- Dados de Exemplo para o NOVO Calendário --- - // (Colado do exemplo do 21st.dev) - const demoEvents: Event[] = [ - { - id: uuidv4(), - title: "Team Standup", - description: "Daily sync with the engineering team.", - startTime: new Date(2025, 9, 20, 9, 0, 0), // Mês 9 = Outubro - endTime: new Date(2025, 9, 20, 9, 30, 0), - color: "blue", - }, - { - id: uuidv4(), - title: "Code Review", - description: "Review PRs for the new feature.", - startTime: new Date(2025, 9, 21, 14, 0, 0), - endTime: new Date(2025, 9, 21, 15, 0, 0), - color: "green", - }, - { - id: uuidv4(), - title: "Client Presentation", - description: "Present the new designs to the client.", - startTime: new Date(2025, 9, 22, 11, 0, 0), - endTime: new Date(2025, 9, 22, 12, 0, 0), - color: "orange", - }, - { - id: uuidv4(), - title: "Sprint Planning", - description: "Plan the next sprint tasks.", - startTime: new Date(2025, 9, 23, 10, 0, 0), - endTime: new Date(2025, 9, 23, 11, 30, 0), - color: "purple", - }, - { - id: uuidv4(), - title: "Doctor Appointment", - description: "Annual check-up.", - startTime: new Date(2025, 9, 24, 16, 0, 0), - endTime: new Date(2025, 9, 24, 17, 0, 0), - color: "red", - }, - { - id: uuidv4(), - title: "Deploy to Production", - description: "Deploy the new release.", - startTime: new Date(2025, 9, 25, 15, 0, 0), - endTime: new Date(2025, 9, 25, 16, 0, 0), - color: "teal", - }, - { - id: uuidv4(), - title: "Product Design Review", - description: "Review the new product design mockups.", - startTime: new Date(2025, 9, 20, 13, 0, 0), - endTime: new Date(2025, 9, 20, 14, 30, 0), - color: "pink", - }, - { - id: uuidv4(), - title: "Gym Session", - description: "Leg day.", - startTime: new Date(2025, 9, 20, 18, 0, 0), - endTime: new Date(2025, 9, 20, 19, 0, 0), - color: "gray", - }, - ]; - // --- Fim dos Dados de Exemplo --- + // --- NOVO ESTADO --- + // Estado para alimentar o NOVO EventManager com dados da API + const [managerEvents, setManagerEvents] = useState([]); -  useEffect(() => { -    document.addEventListener("keydown", (event) => { -      if (event.key === "c") { -        setActiveTab("calendar"); -      } -      if (event.key === "f") { -        setActiveTab("espera"); -      } -      if (event.key === "3") { -        setActiveTab("3d"); -      } -    }); -  }, []); + useEffect(() => { + document.addEventListener("keydown", (event) => { + if (event.key === "c") { + setActiveTab("calendar"); + } + if (event.key === "f") { + setActiveTab("espera"); + } + if (event.key === "3") { + setActiveTab("3d"); + } + }); + }, []); -  useEffect(() => { - // Este useEffect foi mantido, pois ele busca dados para o Calendário 3D -    let mounted = true; -    (async () => { -      try { -        const api = await import('@/lib/api'); -        const arr = await api.listarAgendamentos('select=*&order=scheduled_at.desc&limit=500').catch(() => []); -        if (!mounted) return; -        if (!arr || !arr.length) { -          setAppointments([]); -          // setRequestsList([]); // Removido -          setThreeDEvents([]); -          return; -        } + useEffect(() => { + let mounted = true; + (async () => { + try { + const api = await import('@/lib/api'); + const arr = await api.listarAgendamentos('select=*&order=scheduled_at.desc&limit=500').catch(() => []); + if (!mounted) return; + if (!arr || !arr.length) { + setAppointments([]); + setThreeDEvents([]); + setManagerEvents([]); // Limpa o novo calendário + return; + } -        const patientIds = Array.from(new Set(arr.map((a: any) => a.patient_id).filter(Boolean))); -        const patients = (patientIds && patientIds.length) ? await api.buscarPacientesPorIds(patientIds) : []; -        const patientsById: Record = {}; -        (patients || []).forEach((p: any) => { if (p && p.id) patientsById[String(p.id)] = p; }); + const patientIds = Array.from(new Set(arr.map((a: any) => a.patient_id).filter(Boolean))); + const patients = (patientIds && patientIds.length) ? await api.buscarPacientesPorIds(patientIds) : []; + const patientsById: Record = {}; + (patients || []).forEach((p: any) => { if (p && p.id) patientsById[String(p.id)] = p; }); -        setAppointments(arr || []); + setAppointments(arr || []); - // --- Mapeamento para o FullCalendar (ANTIGO) - REMOVIDO --- -        // const events: EventInput[] = (arr || []).map((obj: any) => { -        //   ... -        // }); -        // setRequestsList(events || []); + // --- LÓGICA DE TRANSFORMAÇÃO PARA O NOVO EVENTMANAGER --- + const newManagerEvents: Event[] = (arr || []).map((obj: any) => { + const scheduled = obj.scheduled_at || obj.scheduledAt || obj.time || null; + const start = scheduled ? new Date(scheduled) : new Date(); + const duration = Number(obj.duration_minutes ?? obj.duration ?? 30) || 30; + const end = new Date(start.getTime() + duration * 60 * 1000); + + const patient = (patientsById[String(obj.patient_id)]?.full_name) || obj.patient_name || obj.patient_full_name || obj.patient || 'Paciente'; + const title = `${patient}: ${obj.appointment_type ?? obj.type ?? ''}`.trim(); + + let color = "gray"; // Cor padrão + if (obj.status === 'confirmed') color = 'green'; + if (obj.status === 'pending') color = 'orange'; - // Convert to 3D calendar events + return { + id: obj.id || uuidv4(), // Usa ID da API ou gera um + title: title, + description: `Agendamento para ${patient}. Status: ${obj.status || 'N/A'}.`, + startTime: start, + endTime: end, + color: color, + }; + }); + setManagerEvents(newManagerEvents); + // --- FIM DA LÓGICA --- + + // Convert to 3D calendar events (MANTIDO 100%) const threeDEvents: CalendarEvent[] = (arr || []).map((obj: any) => { const scheduled = obj.scheduled_at || obj.scheduledAt || obj.time || null; const patient = (patientsById[String(obj.patient_id)]?.full_name) || obj.patient_name || obj.patient_full_name || obj.patient || 'Paciente'; @@ -177,123 +119,109 @@ export default function AgendamentoPage() { } catch (err) { console.warn('[AgendamentoPage] falha ao carregar agendamentos', err); setAppointments([]); - setRequestsList([]); setThreeDEvents([]); + setManagerEvents([]); // Limpa o novo calendário } })(); return () => { mounted = false; }; }, []); -  // Handlers mantidos -  const handleSaveAppointment = (appointment: any) => { -    if (appointment.id) { -      setAppointments((prev) => -        prev.map((a) => (a.id === appointment.id ? appointment : a)) -      ); -    } else { -      const newAppointment = { -        ...appointment, -        id: Date.now().toString(), -      }; -      setAppointments((prev) => [...prev, newAppointment]); -    } -  }; + // Handlers mantidos + const handleSaveAppointment = (appointment: any) => { + if (appointment.id) { + setAppointments((prev) => + prev.map((a) => (a.id === appointment.id ? appointment : a)) + ); + } else { + const newAppointment = { + ...appointment, + id: Date.now().toString(), + }; + setAppointments((prev) => [...prev, newAppointment]); + } + }; -  const handleNotifyPatient = (patientId: string) => { -    console.log(`Notificando paciente ${patientId}`); -  }; + const handleNotifyPatient = (patientId: string) => { + console.log(`Notificando paciente ${patientId}`); + }; -  const handleAddEvent = (event: CalendarEvent) => { -    setThreeDEvents((prev) => [...prev, event]); -  }; + const handleAddEvent = (event: CalendarEvent) => { + setThreeDEvents((prev) => [...prev, event]); + }; -  const handleRemoveEvent = (id: string) => { -    setThreeDEvents((prev) => prev.filter((e) => e.id !== id)); -  }; + const handleRemoveEvent = (id: string) => { + setThreeDEvents((prev) => prev.filter((e) => e.id !== id)); + }; -  return ( -   
-     
-       
-         
+ return ( +
+
+
+
{/* Todo o cabeçalho foi mantido */} -           
-             

-                {activeTab === "calendar" ? "Calendário" : activeTab === "3d" ? "Calendário 3D" : "Lista de Espera"} -             

-             

-                Navegue através dos atalhos: Calendário (C), Fila de espera (F) ou 3D (3). -             

-           
-           
-              -                -                  Opções » -                -                -                  -                    Agendamento -                  -                  -                    Procedimento -                  -                  -                    Financeiro -                  -                -              +
+

+ {activeTab === "calendar" ? "Calendário" : activeTab === "3d" ? "Calendário 3D" : "Lista de Espera"} +

+

+ Navegue através dos atalhos: Calendário (C), Fila de espera (F) ou 3D (3). +

+
+
+ + + Opções » + + + + Agendamento + + + Procedimento + + + Financeiro + + + -             
-                +
+ -                + -                -             
-           
-         
+ +
+
+
+ {/* --- AQUI ESTÁ A SUBSTITUIÇÃO --- */} {activeTab === "calendar" ? (
- { - info.view.calendar.changeView("timeGridDay", info.dateStr); - }} - selectable={true} - selectMirror={true} - dayMaxEvents={true} - dayMaxEventRows={3} - /> + {/* O FullCalendar foi substituído pelo EventManager, + agora alimentado pelo estado dinâmico 'managerEvents' */} +
) : activeTab === "3d" ? ( + // O calendário 3D (ThreeDWallCalendar) foi MANTIDO 100%
) : ( + // A Lista de Espera foi MANTIDA