diff --git a/susconecta/app/(main-routes)/calendar/page.tsx b/susconecta/app/(main-routes)/calendar/page.tsx index 5c54c1c..d0373f1 100644 --- a/susconecta/app/(main-routes)/calendar/page.tsx +++ b/susconecta/app/(main-routes)/calendar/page.tsx @@ -38,6 +38,7 @@ 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); useEffect(() => { document.addEventListener("keydown", (event) => { @@ -57,6 +58,7 @@ export default function AgendamentoPage() { let mounted = true; (async () => { try { + setManagerLoading(true); const api = await import('@/lib/api'); const arr = await api.listarAgendamentos('select=*&order=scheduled_at.desc&limit=500').catch(() => []); if (!mounted) return; @@ -64,6 +66,7 @@ export default function AgendamentoPage() { setAppointments([]); setThreeDEvents([]); setManagerEvents([]); // Limpa o novo calendário + setManagerLoading(false); return; } @@ -98,6 +101,7 @@ export default function AgendamentoPage() { }; }); setManagerEvents(newManagerEvents); + setManagerLoading(false); // --- FIM DA LÓGICA --- // Convert to 3D calendar events (MANTIDO 100%) @@ -121,6 +125,7 @@ export default function AgendamentoPage() { setAppointments([]); setThreeDEvents([]); setManagerEvents([]); // Limpa o novo calendário + setManagerLoading(false); } })(); return () => { mounted = false; }; @@ -216,9 +221,19 @@ export default function AgendamentoPage() { {/* --- AQUI ESTÁ A SUBSTITUIÇÃO --- */} {activeTab === "calendar" ? (
- {/* O FullCalendar foi substituído pelo EventManager, - agora alimentado pelo estado dinâmico 'managerEvents' */} - + {/* 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 +
+ +
+ )} +
) : activeTab === "3d" ? ( // O calendário 3D (ThreeDWallCalendar) foi MANTIDO 100% diff --git a/susconecta/components/Calendario/Calendar.tsx b/susconecta/components/Calendario/Calendar.tsx new file mode 100644 index 0000000..96230cd --- /dev/null +++ b/susconecta/components/Calendario/Calendar.tsx @@ -0,0 +1,118 @@ +import React, { useState, useCallback, useMemo } from "react"; +import { EventCard } from "./EventCard"; +import { Card } from "@/components/ui/card"; + +// Types +import { Event } from "@/components/event-manager"; + +// Week View Component +export function WeekView({ + currentDate, + events, + onEventClick, + onDragStart, + onDragEnd, + onDrop, + getColorClasses, +}: { + currentDate: Date; + events: Event[]; + onEventClick: (event: Event) => void; + onDragStart: (event: Event) => void; + onDragEnd: () => void; + onDrop: (date: Date, hour: number) => void; + getColorClasses: (color: string) => { bg: string; text: string }; +}) { + const startOfWeek = new Date(currentDate); + startOfWeek.setDate(currentDate.getDay()); + + const weekDays = Array.from({ length: 7 }, (_, i) => { + const day = new Date(startOfWeek); + day.setDate(startOfWeek.getDate() + i); + return day; + }); + + const hours = Array.from({ length: 24 }, (_, i) => i); + + const getEventsForDayAndHour = (date: Date, hour: number) => { + return events.filter((event) => { + const eventDate = new Date(event.startTime); + const eventHour = eventDate.getHours(); + return ( + eventDate.getDate() === date.getDate() && + eventDate.getMonth() === date.getMonth() && + eventDate.getFullYear() === date.getFullYear() && + eventHour === hour + ); + }); + }; + + // dias da semana em pt-BR (abreviações) + const weekDayNames = ["Dom", "Seg", "Ter", "Qua", "Qui", "Sex", "Sáb"]; + + return ( + +
+
+ Hora +
+ {weekDays.map((day) => ( +
+
+ {day.toLocaleDateString("pt-BR", { weekday: "short" })} +
+
+ {day.toLocaleDateString("pt-BR", { weekday: "narrow" })} +
+
+ {day.toLocaleDateString("pt-BR", { + month: "short", + day: "numeric", + })} +
+
+ ))} +
+
+ {hours.map((hour) => ( + +
+ {hour.toString().padStart(2, "0")}:00 +
+ {weekDays.map((day) => { + const dayEvents = getEventsForDayAndHour(day, hour); + return ( +
e.preventDefault()} + onDrop={() => onDrop(day, hour)} + > +
+ {dayEvents.map((event) => ( + + ))} +
+
+ ); + })} +
+ ))} +
+
+ ); +} \ No newline at end of file diff --git a/susconecta/components/Calendario/EventCard.tsx b/susconecta/components/Calendario/EventCard.tsx new file mode 100644 index 0000000..175c789 --- /dev/null +++ b/susconecta/components/Calendario/EventCard.tsx @@ -0,0 +1,103 @@ +import React, { useState } from "react"; +import { Event } from "@/components/event-manager"; +import { cn } from "@/lib/utils"; + +/* + Componente leve para representar um evento no calendário. + Compatível com o uso em Calendar.tsx (WeekView / DayView). +*/ + +export function EventCard({ + event, + onEventClick, + onDragStart, + onDragEnd, + getColorClasses, + variant = "default", +}: { + event: Event; + onEventClick: (e: Event) => void; + onDragStart: (e: Event) => void; + onDragEnd: () => void; + getColorClasses: (color: string) => { bg: string; text: string }; + variant?: "default" | "compact" | "detailed"; +}) { + const [hover, setHover] = useState(false); + const color = getColorClasses?.(event.color) ?? { bg: "bg-slate-400", text: "text-white" }; + + const handleDragStart = (e: React.DragEvent) => { + e.dataTransfer.setData("text/plain", event.id); + onDragStart && onDragStart(event); + }; + + const handleClick = () => { + onEventClick && onEventClick(event); + }; + + if (variant === "compact") { + return ( +
onDragEnd && onDragEnd()} + onClick={handleClick} + onMouseEnter={() => setHover(true)} + onMouseLeave={() => setHover(false)} + className={cn( + "rounded px-2 py-0.5 text-xs font-medium truncate", + color.bg, + color.text, + "cursor-pointer transition-all", + hover && "shadow-md scale-105" + )} + > + {event.title} +
+ ); + } + + if (variant === "detailed") { + return ( +
onDragEnd && onDragEnd()} + onClick={handleClick} + onMouseEnter={() => setHover(true)} + onMouseLeave={() => setHover(false)} + className={cn( + "rounded-lg p-2 text-sm cursor-pointer transition-all", + color.bg, + color.text, + hover && "shadow-lg scale-[1.02]" + )} + > +
{event.title}
+ {event.description &&
{event.description}
} +
+ {event.startTime?.toLocaleTimeString?.("pt-BR", { hour: "2-digit", minute: "2-digit" }) ?? ""} - {event.endTime?.toLocaleTimeString?.("pt-BR", { hour: "2-digit", minute: "2-digit" }) ?? ""} +
+
+ ); + } + + // default + return ( +
onDragEnd && onDragEnd()} + onClick={handleClick} + onMouseEnter={() => setHover(true)} + onMouseLeave={() => setHover(false)} + className={cn( + "relative rounded px-2 py-1 text-xs font-medium cursor-pointer transition-all", + color.bg, + color.text, + hover && "shadow-md scale-105" + )} + > +
{event.title}
+
+ ); +} diff --git a/susconecta/components/event-manager.tsx b/susconecta/components/event-manager.tsx index 6373718..1a19417 100644 --- a/susconecta/components/event-manager.tsx +++ b/susconecta/components/event-manager.tsx @@ -1,6 +1,6 @@ "use client" -import { useState, useCallback, useMemo } from "react" +import React, { useState, useCallback, useMemo } from "react" import { Button } from "@/components/ui/button" import { Card } from "@/components/ui/card" import { Input } from "@/components/ui/input" @@ -263,30 +263,30 @@ export function EventManager({

{view === "month" && - currentDate.toLocaleDateString("en-US", { + currentDate.toLocaleDateString("pt-BR", { month: "long", year: "numeric", })} {view === "week" && - `Week of ${currentDate.toLocaleDateString("en-US", { + `Semana de ${currentDate.toLocaleDateString("pt-BR", { month: "short", day: "numeric", })}`} {view === "day" && - currentDate.toLocaleDateString("en-US", { + currentDate.toLocaleDateString("pt-BR", { weekday: "long", month: "long", day: "numeric", year: "numeric", })} - {view === "list" && "All Events"} + {view === "list" && "Todos os eventos"}

@@ -378,7 +378,7 @@ export function EventManager({ className="w-full sm:w-auto" > - New Event + Novo Evento
@@ -387,7 +387,7 @@ export function EventManager({
setSearchQuery(e.target.value)} className="pl-9" @@ -412,7 +412,7 @@ export function EventManager({ - Filter by Color + Filtrar por Cor {colors.map((color) => ( - Filter by Tag + Filtrar por Tag {availableTags.map((tag) => ( - Filter by Category + Filtrar por Categoria {categories.map((category) => ( - Clear Filters + Limpar Filtros )}
@@ -525,7 +525,7 @@ export function EventManager({ - Filter by Color + Filtrar por Cor {colors.map((color) => ( - Filter by Tag + Filtrar por Tag {availableTags.map((tag) => ( - Filter by Category + Filtrar por Categoria {categories.map((category) => ( - Clear + Limpar )} @@ -628,7 +628,7 @@ export function EventManager({ {hasActiveFilters && (
- Active filters: + Filtros ativos: {selectedColors.map((colorValue) => { const color = getColorClasses(colorValue) return ( @@ -678,8 +678,8 @@ export function EventManager({ setSelectedEvent(event) setIsDialogOpen(true) }} - onDragStart={handleDragStart} - onDragEnd={handleDragEnd} + onDragStart={(event) => handleDragStart(event)} + onDragEnd={() => handleDragEnd()} onDrop={handleDrop} getColorClasses={getColorClasses} /> @@ -693,8 +693,8 @@ export function EventManager({ setSelectedEvent(event) setIsDialogOpen(true) }} - onDragStart={handleDragStart} - onDragEnd={handleDragEnd} + onDragStart={(event) => handleDragStart(event)} + onDragEnd={() => handleDragEnd()} onDrop={handleDrop} getColorClasses={getColorClasses} /> @@ -708,8 +708,8 @@ export function EventManager({ setSelectedEvent(event) setIsDialogOpen(true) }} - onDragStart={handleDragStart} - onDragEnd={handleDragEnd} + onDragStart={(event) => handleDragStart(event)} + onDragEnd={() => handleDragEnd()} onDrop={handleDrop} getColorClasses={getColorClasses} /> @@ -730,15 +730,15 @@ export function EventManager({ - {isCreating ? "Create Event" : "Event Details"} + {isCreating ? "Criar Evento" : "Detalhes do Evento"} - {isCreating ? "Add a new event to your calendar" : "View and edit event details"} + {isCreating ? "Adicione um novo evento ao seu calendário" : "Visualizar e editar detalhes do evento"}
- + ({ ...prev, title: e.target.value })) : setSelectedEvent((prev) => (prev ? { ...prev, title: e.target.value } : null)) } - placeholder="Event title" + placeholder="Título do evento" />
- +