"use client" import { useEffect, useState } from 'react' import { nanoid } from 'nanoid' import { Button } from '@/components/ui/button' import { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle } from '@/components/ui/dialog' import { Input } from '@/components/ui/input' import { Card } from '@/components/ui/card' import { RecurrencePicker } from '@/components/recurrence-picker' import { IcsFilePicker } from '@/components/ics-file-picker' import { saveEvent as addEvent, deleteEvent, getEvents as getAllEvents, clearEvents, updateEvent } from '@/lib/events-db' import { parseICS, generateICS } from '@/lib/ical' import type { CalendarEvent } from '@/lib/types' import { Textarea } from '@/components/ui/textarea' import { useSession } from 'next-auth/react' import { toast } from 'sonner' export default function HomePage() { const [events, setEvents] = useState([]) const [dialogOpen, setDialogOpen] = useState(false) const [editingId, setEditingId] = useState(null) const [isDragOver, setIsDragOver] = useState(false) // Form fields const [title, setTitle] = useState('') const [description, setDescription] = useState('') const [location, setLocation] = useState('') const [url, setUrl] = useState('') const [start, setStart] = useState('') const [end, setEnd] = useState('') const [allDay, setAllDay] = useState(false) const [recurrenceRule, setRecurrenceRule] = useState(undefined) // AI const [aiPrompt, setAiPrompt] = useState('') const [aiLoading, setAiLoading] = useState(false) const [summary, setSummary] = useState(null) const [summaryUpdated, setSummaryUpdated] = useState(null) useEffect(() => { (async () => { const stored = await getAllEvents() setEvents(stored) })() }, []) const { data: session, status } = useSession() const resetForm = () => { setTitle('') setDescription('') setLocation('') setUrl('') setStart('') setEnd('') setAllDay(false) setEditingId(null) } const handleSave = async () => { const eventData: CalendarEvent = { id: editingId || nanoid(), title, description, location, url, recurrenceRule, start, end: end || undefined, allDay, createdAt: editingId ? events.find(e => e.id === editingId)?.createdAt : new Date().toISOString(), lastModified: new Date().toISOString(), } if (editingId) { await updateEvent(eventData) setEvents(prev => prev.map(e => (e.id === editingId ? eventData : e))) } else { await addEvent(eventData) setEvents(prev => [...prev, eventData]) } resetForm() setDialogOpen(false) } const handleDelete = async (id: string) => { await deleteEvent(id) setEvents(prev => prev.filter(e => e.id !== id)) } const handleClearAll = async () => { await clearEvents() setEvents([]) } const handleImport = async (file: File) => { const text = await file.text() const parsed = parseICS(text) for (const ev of parsed) { await addEvent(ev) } const stored = await getAllEvents() setEvents(stored) } const handleExport = () => { const icsData = generateICS(events) const blob = new Blob([icsData], { type: 'text/calendar' }) const url = URL.createObjectURL(blob) const a = document.createElement('a') a.href = url a.download = `icallocal-export-${new Date().toLocaleTimeString()}.ics` document.body.appendChild(a) a.click() document.body.removeChild(a) URL.revokeObjectURL(url) } // Drag-and-drop const handleDragOver = (e: React.DragEvent) => { e.preventDefault(); setIsDragOver(true) } const handleDragLeave = (e: React.DragEvent) => { e.preventDefault(); setIsDragOver(false) } const handleDrop = (e: React.DragEvent) => { e.preventDefault() setIsDragOver(false) if (e.dataTransfer.files?.length) { const file = e.dataTransfer.files[0] if (file.name.endsWith('.ics')) { handleImport(file) } else { toast.warning('Please drop an .ics file') } } } // AI Create Event const handleAiCreate = async () => { if (!aiPrompt.trim()) return setAiLoading(true) const promise = (): Promise<{ message: string }> => new Promise(async (resolve, reject) => { try { const res = await fetch('/api/ai-event', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ prompt: aiPrompt }) }) if (res.status === 401) { setAiLoading(false) reject({ message: 'Please sign in to use AI features.' }) return } const data = await res.json() if (Array.isArray(data) && data.length > 0) { if (data.length === 1) { // Prefill dialog directly (same as before) const ev = data[0] setTitle(ev.title || '') setDescription(ev.description || '') setLocation(ev.location || '') setUrl(ev.url || '') setStart(ev.start || '') setEnd(ev.end || '') setAllDay(ev.allDay || false) setEditingId(null) setAiPrompt("") setDialogOpen(true) setRecurrenceRule(ev.recurrenceRule || undefined) resolve({ message: 'Event has been created!' }) } else { // Save them all directly to DB for (const ev of data) { const newEvent = { id: nanoid(), ...ev, createdAt: new Date().toISOString(), lastModified: new Date().toISOString(), } await addEvent(newEvent) } const stored = await getAllEvents() setEvents(stored) setAiPrompt("") setSummary(`Added ${data.length} AI-generated events.`) setSummaryUpdated(new Date().toLocaleString()) resolve({ message: 'Event has been created!' }) } } else { reject({ message: 'AI did not return event data.' }) } } catch (err) { console.error(err) reject({ message: 'Error from AI service.' }) } }) toast.promise(promise, { loading: "Generating event...", success: ({ message }) => { return message }, error: ({ message }) => { return message } }) setAiLoading(false) } // AI Summarize Events const handleAiSummarize = async () => { if (!events.length) { setSummary("No events to summarize.") setSummaryUpdated(new Date().toLocaleString()) return } setAiLoading(true) try { const res = await fetch('/api/ai-summary', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ events }) }) const data = await res.json() if (data.summary) { setSummary(data.summary) setSummaryUpdated(new Date().toLocaleString()) } else { setSummary("No summary generated.") setSummaryUpdated(new Date().toLocaleString()) } } catch { setSummary("Error summarizing events") setSummaryUpdated(new Date().toLocaleString()) } finally { setAiLoading(false) } } return (
{/* AI Toolbar */} {status === "loading" ?
Loading...
:
{session?.user ? (