import React, { useState, useMemo, useEffect } from 'react'; import { format, startOfMonth, endOfMonth, startOfWeek, endOfWeek, eachDayOfInterval, isSameMonth, isSameDay, addMonths, isToday, parseISO } from 'date-fns'; import { motion, AnimatePresence, PanInfo } from 'framer-motion'; import { ChevronLeft, ChevronRight, Plus, LogOut, Settings as SettingsIcon, Sparkles, Bot, Moon, Sun, Zap, Cloud, Wand2, Trash2, Calendar as CalIcon, Activity, X, ChevronDown } from 'lucide-react'; import { CalendarEvent, UserRole, UserStatus, AppSettings } from '../types'; import { Button } from './Button'; import { analyzeScheduleAndSuggest } from '../services/aiService'; import { Candle } from './Candle'; import { CosmicPulseModal } from './CosmicPulseModal'; interface CalendarViewProps { events: CalendarEvent[]; currentUser: UserRole; currentStatus: UserStatus; onStatusChange: (status: UserStatus) => void; onAddEvent: (date?: Date) => void; onEditEvent: (event: CalendarEvent) => void; onMoveEvent: (eventId: string, newDate: Date) => void; onLogout: () => void; onDeleteEvent: (id: string) => void; onOpenSettings: () => void; onOpenAssistant: () => void; settings: AppSettings; } export const CalendarView: React.FC = ({ events, currentUser, currentStatus, onStatusChange, onAddEvent, onEditEvent, onLogout, onDeleteEvent, onMoveEvent, onOpenSettings, onOpenAssistant, settings }) => { const [currentDate, setCurrentDate] = useState(new Date()); const [selectedDate, setSelectedDate] = useState(new Date()); const [direction, setDirection] = useState(0); const [showOracle, setShowOracle] = useState(false); const [oracleWisdom, setOracleWisdom] = useState([]); const [isConsulting, setIsConsulting] = useState(false); const [isEditMode, setIsEditMode] = useState(false); const [dragOverDate, setDragOverDate] = useState(null); const [isPulseOpen, setIsPulseOpen] = useState(false); const [isAgendaOpen, setIsAgendaOpen] = useState(false); // New state to control sheet visibility // Mock partner const partnerRole: UserRole = currentUser === 'mom' ? 'dad' : 'mom'; const [partnerStatus, setPartnerStatus] = useState('offline'); useEffect(() => { const partnerTimer = setInterval(() => { const statuses: UserStatus[] = ['online', 'busy', 'casting_spells', 'thinking']; setPartnerStatus(statuses[Math.floor(Math.random() * statuses.length)]); }, 10000); return () => clearInterval(partnerTimer); }, []); const days = useMemo(() => { const monthStart = startOfMonth(currentDate); const startDate = startOfWeek(monthStart); const endDate = endOfWeek(endOfMonth(monthStart)); return eachDayOfInterval({ start: startDate, end: endDate }); }, [currentDate]); const changeMonth = (val: number) => { setDirection(val); setCurrentDate(prev => addMonths(prev, val)); }; const getDayEvents = (day: Date) => { const dayEvents = events.filter(event => isSameDay(parseISO(event.startTime), day)); return dayEvents.sort((a, b) => { if (a.category === 'magic' && b.category !== 'magic') return -1; if (a.category !== 'magic' && b.category === 'magic') return 1; return new Date(a.startTime).getTime() - new Date(b.startTime).getTime(); }); }; const handleConsultOracle = async () => { if (showOracle) { setShowOracle(false); return; } setIsConsulting(true); setShowOracle(true); const wisdom = await analyzeScheduleAndSuggest(events, settings); setOracleWisdom(wisdom); setIsConsulting(false); }; const getStatusIcon = (status: UserStatus) => { switch (status) { case 'online': return ; case 'busy': return ; case 'sleeping': return ; case 'casting_spells': return ; case 'thinking': return ; default: return
; } }; const statusOptions: {id: UserStatus, label: string, icon: any}[] = [ { id: 'online', label: 'Present', icon: Sun }, { id: 'busy', label: 'Busy', icon: Zap }, { id: 'casting_spells', label: 'Magic', icon: Sparkles }, { id: 'thinking', label: 'Thinking', icon: Cloud }, { id: 'sleeping', label: 'Resting', icon: Moon }, ]; const handleDragStart = (e: React.DragEvent, eventId: string) => { if (!isEditMode) return; e.dataTransfer.setData('eventId', eventId); }; const handleDragOver = (e: React.DragEvent, dateStr: string) => { if (!isEditMode) return; e.preventDefault(); setDragOverDate(dateStr); }; const handleDrop = (e: React.DragEvent, date: Date) => { if (!isEditMode) return; e.preventDefault(); const eventId = e.dataTransfer.getData('eventId'); if (eventId) onMoveEvent(eventId, date); setDragOverDate(null); }; const handleTrashDrop = (e: React.DragEvent) => { if (!isEditMode) return; e.preventDefault(); const eventId = e.dataTransfer.getData('eventId'); if (eventId) onDeleteEvent(eventId); }; const handleDayClick = (day: Date) => { setSelectedDate(day); setIsAgendaOpen(true); // Open sheet on tap if (isSameDay(day, selectedDate) && !isEditMode) { // Optional double tap behavior logic } }; const handleSheetDragEnd = (event: MouseEvent | TouchEvent | PointerEvent, info: PanInfo) => { if (info.offset.y > 100 || info.velocity.y > 500) { setIsAgendaOpen(false); } }; const selectedDayEvents = getDayEvents(selectedDate); return (
{/* Top Bar - Stacked on Mobile */}
{/* Status Bar */}
{getStatusIcon(currentStatus)}
{currentUser.toUpperCase()}
{statusOptions.map(opt => ( ))}
{partnerRole.toUpperCase()}
{getStatusIcon(partnerStatus)}
{/* Title */}

Celestial

{/* Tools */}
{/* Main Glass Panel */}
{/* Calendar Navigation */}

setCurrentDate(new Date())}> {format(currentDate, 'MMMM yyyy')}

{/* Oracle Popup */} {showOracle && (

The Stars Suggest...

{isConsulting ? (

Gazing into the void...

) : (
{oracleWisdom.map((wis, i) => (
onAddEvent()}> {wis}
))}
)}
)}
{/* Days Header */}
{['S','M','T','W','T','F','S'].map(d => (
{d}
))}
{/* Calendar Grid */}
({ x: d > 0 ? 50 : -50, opacity: 0 }), center: { x: 0, opacity: 1 }, exit: (d) => ({ x: d > 0 ? -50 : 50, opacity: 0 }) }} initial="enter" animate="center" exit="exit" transition={{ duration: 0.3 }} className="grid grid-cols-7 grid-rows-5 h-full gap-1 md:gap-2 pb-20 md:pb-0" > {days.map((day) => { const dayEvents = getDayEvents(day); const isCurrentMonth = isSameMonth(day, currentDate); const isSelected = isSameDay(day, selectedDate); const isTodayDate = isToday(day); const dayKey = day.toISOString(); const isDragOver = dragOverDate === dayKey; return (
handleDayClick(day)} onDragOver={(e) => handleDragOver(e, dayKey)} onDrop={(e) => handleDrop(e, day)} className={` relative p-1 md:p-2 rounded-xl border transition-all duration-200 flex flex-col items-center justify-start ${isSelected ? 'bg-white/10 border-purple-500/50' : 'border-transparent'} ${isDragOver ? 'bg-purple-500/30 border-purple-400 scale-105 z-10' : ''} ${isCurrentMonth ? 'opacity-100' : 'opacity-30'} ${isTodayDate ? 'shadow-[0_0_10px_rgba(168,85,247,0.3)] border-purple-500/30' : ''} `} > {format(day, 'd')} {/* Desktop View: Text List */}
{dayEvents.map(ev => (
handleDragStart(e, ev.id)} onClick={(e) => { e.stopPropagation(); onEditEvent(ev); }} className={` text-[10px] px-1 rounded truncate cursor-pointer hover:brightness-110 ${ev.category === 'date' ? 'bg-red-900/60 text-red-200' : ev.category === 'magic' ? 'bg-gradient-to-r from-purple-600 to-blue-600 text-white' : 'bg-gray-800/80 text-gray-300'} `} > {ev.title}
))}
{/* Mobile View: Dots */}
{dayEvents.slice(0, 4).map(ev => (
))} {dayEvents.length > 4 &&
}
) })}
{/* Mobile Agenda Sheet (Replaces inline list) */} {selectedDate && isAgendaOpen && ( {/* Drag Handle */}
setIsAgendaOpen(false)} >
{/* Sheet Header */}

{format(selectedDate, 'EEEE')}

{format(selectedDate, 'MMMM do')}

{/* Events List */}
{selectedDayEvents.length === 0 ? (

No cosmic events planned.

) : ( selectedDayEvents.map(ev => ( onEditEvent(ev)} className="bg-white/5 border border-white/5 p-4 rounded-2xl flex items-center gap-4 active:scale-95 transition-transform shadow-sm" >

{ev.title}

{format(parseISO(ev.startTime), 'h:mm a')} {ev.category}

)) )}
)} {/* Edit/Trash Actions */}
{/* FAB (Visible on Desktop OR when Mobile Agenda is closed) */} {!isEditMode && (!isAgendaOpen || window.innerWidth >= 768) && ( )}
{/* The Void (Trash) */} {isEditMode && (
e.preventDefault()} onDrop={handleTrashDrop} className="w-20 h-20 md:w-24 md:h-24 rounded-full bg-black border-2 border-red-900 shadow-[0_0_40px_rgba(255,0,0,0.3)] flex items-center justify-center relative overflow-hidden group backdrop-blur-md" >
Void
)}
{/* Pulse Modal */} setIsPulseOpen(false)} events={events} settings={settings} />
); };