"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 { Popover, PopoverTrigger, PopoverContent } from "@/components/ui/popover" import { HoverCard, HoverCardTrigger, HoverCardContent } from "@/components/ui/hover-card" import { Trash2 } 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 } interface ThreeDWallCalendarProps { events: CalendarEvent[] onAddEvent?: (e: CalendarEvent) => void onRemoveEvent?: (id: string) => void panelWidth?: number panelHeight?: number columns?: number } export function ThreeDWallCalendar({ events, onAddEvent, onRemoveEvent, 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 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) // 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")) // 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 dragStart.current = { x: e.clientX, y: e.clientY } ;(e.currentTarget as Element).setPointerCapture(e.pointerId) // ✅ Correct element } 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 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 } const gap = 12 const rowCount = Math.ceil(days.length / columns) const wallCenterRow = (rowCount - 1) / 2 return (
{format(dateRef, "MMMM yyyy", { locale: ptBR })}
{/* Wall container */}
{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 (
{format(day, "d")}
{format(day, "EEE", { locale: ptBR })}
{/* events */}
{dayEvents.map((ev, i) => { const left = 8 + (i * 34) % (panelWidth - 40) const top = 8 + Math.floor((i * 34) / (panelWidth - 40)) * 28 return (
{ev.title}
{ev.title}
{format(new Date(ev.date), "PPP p", { locale: ptBR })}
{onRemoveEvent && ( )}
) })}
{dayEvents.length} evento(s)
) })}
{/* Add event form */}
setTitle(e.target.value)} /> setNewDate(e.target.value)} />
) }