diff --git a/susconecta/app/(main-routes)/calendar/page.tsx b/susconecta/app/(main-routes)/calendar/page.tsx index d2349f5..519ba4b 100644 --- a/susconecta/app/(main-routes)/calendar/page.tsx +++ b/susconecta/app/(main-routes)/calendar/page.tsx @@ -2,7 +2,6 @@ // Imports mantidos import { useEffect, useState } from "react"; -import dynamic from "next/dynamic"; // --- Imports do EventManager (NOVO) - MANTIDOS --- import { EventManager, type Event } from "@/components/features/general/event-manager"; @@ -10,22 +9,21 @@ import { v4 as uuidv4 } from 'uuid'; // Usado para IDs de fallback // Imports mantidos import { Button } from "@/components/ui/button"; -import { useAuth } from "@/hooks/useAuth"; -import { mockWaitingList } from "@/lib/mocks/appointment-mocks"; import "./index.css"; -import { ThreeDWallCalendar, CalendarEvent } from "@/components/ui/three-dwall-calendar"; // Calendário 3D mantido -import { PatientRegistrationForm } from "@/components/features/forms/patient-registration-form"; - -const ListaEspera = dynamic( - () => import("@/components/features/agendamento/ListaEspera"), - { ssr: false } -); export default function AgendamentoPage() { - const { user, token } = useAuth(); const [appointments, setAppointments] = useState([]); - const [activeTab, setActiveTab] = useState<"calendar" | "3d">("calendar"); - const [threeDEvents, setThreeDEvents] = useState([]); + // REMOVIDO: abas e 3D → não há mais alternância de abas + // const [activeTab, setActiveTab] = useState<"calendar" | "3d">("calendar"); + + // REMOVIDO: estados do 3D e formulário do paciente (eram usados pelo 3D) + // const [threeDEvents, setThreeDEvents] = useState([]); + // const [showPatientForm, setShowPatientForm] = useState(false); + + // --- NOVO ESTADO --- + // Estado para alimentar o NOVO EventManager com dados da API + const [managerEvents, setManagerEvents] = useState([]); + const [managerLoading, setManagerLoading] = useState(true); // Padroniza idioma da página para pt-BR (afeta componentes que usam o lang do documento) useEffect(() => { @@ -42,21 +40,6 @@ export default function AgendamentoPage() { } }, []); - // --- NOVO ESTADO --- - // Estado para alimentar o NOVO EventManager com dados da API - const [managerEvents, setManagerEvents] = useState([]); - const [managerLoading, setManagerLoading] = useState(true); - - // Estado para o formulário de registro de paciente - const [showPatientForm, setShowPatientForm] = useState(false); - - useEffect(() => { - document.addEventListener("keydown", (event) => { - if (event.key === "c") setActiveTab("calendar"); - if (event.key === "3") setActiveTab("3d"); - }); - }, []); - useEffect(() => { let mounted = true; (async () => { @@ -67,8 +50,8 @@ export default function AgendamentoPage() { if (!mounted) return; if (!arr || !arr.length) { setAppointments([]); - setThreeDEvents([]); - setManagerEvents([]); // Limpa o novo calendário + // REMOVIDO: setThreeDEvents([]) + setManagerEvents([]); setManagerLoading(false); return; } @@ -86,12 +69,11 @@ export default function AgendamentoPage() { 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(); - // Mapeamento de cores padronizado: - // azul = solicitado; verde = confirmado; laranja = pendente; vermelho = cancelado; azul como fallback + // Mapeamento de cores padronizado const status = String(obj.status || "").toLowerCase(); let color: Event["color"] = "blue"; if (status === "confirmed" || status === "confirmado") color = "green"; @@ -112,27 +94,12 @@ export default function AgendamentoPage() { setManagerLoading(false); // --- 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'; - const appointmentType = obj.appointment_type ?? obj.type ?? 'Consulta'; - const title = `${patient}: ${appointmentType}`.trim(); - return { - id: obj.id || String(Date.now()), - title, - date: scheduled ? new Date(scheduled).toISOString() : new Date().toISOString(), - status: obj.status || 'pending', - patient, - type: appointmentType, - }; - }); - setThreeDEvents(threeDEvents); + // REMOVIDO: conversão para 3D e setThreeDEvents } catch (err) { console.warn('[AgendamentoPage] falha ao carregar agendamentos', err); setAppointments([]); - setThreeDEvents([]); - setManagerEvents([]); // Limpa o novo calendário + // REMOVIDO: setThreeDEvents([]) + setManagerEvents([]); setManagerLoading(false); } })(); @@ -154,52 +121,22 @@ export default function AgendamentoPage() { } }; - const handleAddEvent = (event: CalendarEvent) => { - setThreeDEvents((prev) => [...prev, event]); - }; - - const handleRemoveEvent = (id: string) => { - setThreeDEvents((prev) => prev.filter((e) => e.id !== id)); - }; - return (
- {/* Todo o cabeçalho foi mantido */} + {/* Cabeçalho simplificado (sem 3D) */}
-

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

+

Calendário

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

-
-
- - - -
-
+ {/* REMOVIDO: botões de abas Calendário/3D */}
- {/* Legenda de status (estilo Google Calendar) */} + {/* Legenda de status (aplica-se ao EventManager) */}
@@ -210,49 +147,31 @@ export default function AgendamentoPage() { Confirmado
+ {/* Novo: Cancelado (vermelho) */} +
+ + Cancelado +
- {/* --- AQUI ESTÁ A SUBSTITUIÇÃO --- */} - {activeTab === "calendar" ? ( -
- {/* mostra loading até managerEvents ser preenchido (API integrada desde a entrada) */} -
- {managerLoading ? ( -
-
Conectando ao calendário — carregando agendamentos...
-
- ) : ( - // EventManager ocupa a área principal e já recebe events da API -
- -
- )} -
+ {/* Apenas o EventManager */} +
+
+ {managerLoading ? ( +
+
Conectando ao calendário — carregando agendamentos...
+
+ ) : ( +
+ +
+ )}
- ) : activeTab === "3d" ? ( - // O calendário 3D (ThreeDWallCalendar) foi MANTIDO 100% -
- setShowPatientForm(true)} - /> -
- ) : null} +
- - {/* Formulário de Registro de Paciente */} - { - console.log('[Calendar] Novo paciente registrado:', newPaciente); - setShowPatientForm(false); - }} - /> + + {/* REMOVIDO: PatientRegistrationForm (era acionado pelo 3D) */}
); diff --git a/susconecta/components/ui/three-dwall-calendar.tsx b/susconecta/components/ui/three-dwall-calendar.tsx deleted file mode 100644 index aa1a7e9..0000000 --- a/susconecta/components/ui/three-dwall-calendar.tsx +++ /dev/null @@ -1,467 +0,0 @@ -"use client" - -import * as React from "react" -import { Card, CardContent } from "@/components/ui/card" -import { Button } from "@/components/ui/button" -import { Input } from "@/components/ui/input" -import { HoverCard, HoverCardTrigger, HoverCardContent } from "@/components/ui/hover-card" -import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription } from "@/components/ui/dialog" -import { Badge } from "@/components/ui/badge" -import { Trash2, Calendar, Clock, User } from "lucide-react" -import { v4 as uuidv4 } from "uuid" -import { startOfMonth, endOfMonth, eachDayOfInterval, format } from "date-fns" -import { ptBR } from "date-fns/locale" - -export type CalendarEvent = { - id: string - title: string - date: string // ISO - status?: 'confirmed' | 'pending' | 'cancelled' | string - patient?: string - type?: string -} - -interface ThreeDWallCalendarProps { - events: CalendarEvent[] - onAddEvent?: (e: CalendarEvent) => void - onRemoveEvent?: (id: string) => void - onOpenAddPatientForm?: () => void - panelWidth?: number - panelHeight?: number - columns?: number -} - -export function ThreeDWallCalendar({ - events, - onAddEvent, - onRemoveEvent, - onOpenAddPatientForm, - panelWidth = 160, - panelHeight = 120, - columns = 7, -}: ThreeDWallCalendarProps) { - const [dateRef, setDateRef] = React.useState(new Date()) - const [title, setTitle] = React.useState("") - const [newDate, setNewDate] = React.useState("") - const [selectedDay, setSelectedDay] = React.useState(null) - const [isDialogOpen, setIsDialogOpen] = React.useState(false) - const wallRef = React.useRef(null) - - // 3D tilt state - const [tiltX, setTiltX] = React.useState(18) - const [tiltY, setTiltY] = React.useState(0) - const isDragging = React.useRef(false) - const dragStart = React.useRef<{ x: number; y: number } | null>(null) - const hasDragged = React.useRef(false) - const clickStart = React.useRef<{ x: number; y: number } | null>(null) - - // month days - const days = eachDayOfInterval({ - start: startOfMonth(dateRef), - end: endOfMonth(dateRef), - }) - - const eventsForDay = (d: Date) => - events.filter((ev) => format(new Date(ev.date), "yyyy-MM-dd") === format(d, "yyyy-MM-dd")) - - const selectedDayEvents = selectedDay ? eventsForDay(selectedDay) : [] - - const handleDayClick = (day: Date) => { - console.log('Day clicked:', format(day, 'dd/MM/yyyy')) - setSelectedDay(day) - setIsDialogOpen(true) - } - - // Add event handler - const handleAdd = () => { - if (!title.trim() || !newDate) return - onAddEvent?.({ - id: uuidv4(), - title: title.trim(), - date: new Date(newDate).toISOString(), - }) - setTitle("") - setNewDate("") - } - - // wheel tilt - const onWheel = (e: React.WheelEvent) => { - setTiltX((t) => Math.max(0, Math.min(50, t + e.deltaY * 0.02))) - setTiltY((t) => Math.max(-45, Math.min(45, t + e.deltaX * 0.05))) - } - - // drag tilt - const onPointerDown = (e: React.PointerEvent) => { - isDragging.current = true - hasDragged.current = false - dragStart.current = { x: e.clientX, y: e.clientY } - ;(e.currentTarget as Element).setPointerCapture(e.pointerId) - } - - const onPointerMove = (e: React.PointerEvent) => { - if (!isDragging.current || !dragStart.current) return - const dx = e.clientX - dragStart.current.x - const dy = e.clientY - dragStart.current.y - - // Se moveu mais de 5 pixels, considera como drag - if (Math.abs(dx) > 5 || Math.abs(dy) > 5) { - hasDragged.current = true - } - - setTiltY((t) => Math.max(-60, Math.min(60, t + dx * 0.1))) - setTiltX((t) => Math.max(0, Math.min(60, t - dy * 0.1))) - dragStart.current = { x: e.clientX, y: e.clientY } - } - - const onPointerUp = () => { - isDragging.current = false - dragStart.current = null - // Reset hasDragged após um curto delay para permitir o clique ser processado - setTimeout(() => { - hasDragged.current = false - }, 100) - } - - const gap = 12 - const rowCount = Math.ceil(days.length / columns) - const wallCenterRow = (rowCount - 1) / 2 - - return ( -
-
-
- -
{format(dateRef, "MMMM yyyy", { locale: ptBR })}
- - {/* Botão Pacientes de hoje */} - -
- - {/* Legenda de cores */} -
-
-
- Confirmado -
-
-
- Pendente -
-
-
- Cancelado -
-
-
- Outros -
-
-
- - {/* Wall container */} -
-
- 💡 Arraste para rotacionar • Scroll para inclinar -
-
-
-
- {days.map((day, idx) => { - const row = Math.floor(idx / columns) - const rowOffset = row - wallCenterRow - const z = Math.max(-80, 40 - Math.abs(rowOffset) * 20) - const dayEvents = eventsForDay(day) - - return ( -
{ - clickStart.current = { x: e.clientX, y: e.clientY } - }} - onPointerUp={(e) => { - if (clickStart.current) { - const dx = Math.abs(e.clientX - clickStart.current.x) - const dy = Math.abs(e.clientY - clickStart.current.y) - // Se moveu menos de 5 pixels, é um clique - if (dx < 5 && dy < 5) { - e.stopPropagation() - handleDayClick(day) - } - clickStart.current = null - } - }} - > - - -
-
{format(day, "d")}
-
- {dayEvents.length > 0 && `${dayEvents.length} ${dayEvents.length === 1 ? 'paciente' : 'pacientes'}`} -
-
-
{format(day, "EEE", { locale: ptBR })}
- - {/* events */} -
- {dayEvents.map((ev, i) => { - // Calcular tamanho da bolinha baseado na quantidade de eventos - const eventCount = dayEvents.length - const ballSize = eventCount <= 3 ? 20 : - eventCount <= 6 ? 16 : - eventCount <= 10 ? 14 : - eventCount <= 15 ? 12 : 10 - - const spacing = ballSize + 4 - const maxPerRow = Math.floor((panelWidth - 16) / spacing) - const col = i % maxPerRow - const row = Math.floor(i / maxPerRow) - const left = 4 + (col * spacing) - const top = 4 + (row * spacing) - - // Cores baseadas no status - const getStatusColor = () => { - switch(ev.status) { - case 'confirmed': return 'bg-green-500 dark:bg-green-600' - case 'pending': return 'bg-yellow-500 dark:bg-yellow-600' - case 'cancelled': return 'bg-red-500 dark:bg-red-600' - default: return 'bg-blue-500 dark:bg-blue-600' - } - } - - return ( - - -
- • -
-
- -
-
{ev.title}
- {ev.patient && ev.type && ( -
-
Paciente: {ev.patient}
-
Tipo: {ev.type}
-
- )} -
- {format(new Date(ev.date), "PPP 'às' p", { locale: ptBR })} -
- {ev.status && ( -
- Status:{' '} - - {ev.status === 'confirmed' ? 'Confirmado' : - ev.status === 'pending' ? 'Pendente' : - ev.status === 'cancelled' ? 'Cancelado' : ev.status} - -
- )} - {onRemoveEvent && ( - - )} -
-
-
- ) - })} -
-
-
-
- ) - })} -
-
-
-
- - {/* Dialog de detalhes do dia */} - - - - {/* Navegação de dias */} -
- - - {selectedDay && format(selectedDay, "dd 'de' MMMM 'de' yyyy", { locale: ptBR })} - - -
- - {selectedDayEvents.length} {selectedDayEvents.length === 1 ? 'paciente agendado' : 'pacientes agendados'} - -
-
- {selectedDayEvents.length === 0 ? ( -
- Nenhum paciente agendado para este dia -
- ) : ( - selectedDayEvents.map((ev) => { - const getStatusColor = () => { - switch(ev.status) { - case 'confirmed': return 'bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200' - case 'pending': return 'bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-200' - case 'cancelled': return 'bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-200' - default: return 'bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-200' - } - } - - const getStatusText = () => { - switch(ev.status) { - case 'confirmed': return 'Confirmado' - case 'pending': return 'Pendente' - case 'cancelled': return 'Cancelado' - default: return ev.status || 'Sem status' - } - } - - return ( - - -
-
-
- -

{ev.patient || ev.title}

-
- - {ev.type && ( -
- - {ev.type} -
- )} - -
- - {format(new Date(ev.date), "HH:mm", { locale: ptBR })} -
- - - {getStatusText()} - -
- - {onRemoveEvent && ( - - )} -
-
-
- ) - }) - )} -
-
-
- - {/* Add event form */} -
- {onOpenAddPatientForm ? ( - - ) : ( - <> - setTitle(e.target.value)} /> - setNewDate(e.target.value)} /> - - - )} -
-
- ) -}