phase 3 - ical import/export

This commit is contained in:
2025-08-14 23:47:19 -04:00
parent b1dad2f6ba
commit 9d53e4da21
4 changed files with 101 additions and 4 deletions

View File

@@ -8,6 +8,7 @@ import { Input } from '@/components/ui/input'
import { type CalendarEvent } from '@/lib/types'
import { addEvent, deleteEvent, getAllEvents, clearEvents } from '@/lib/db'
import { parseICS, generateICS } from '@/lib/ical'
export default function HomePage() {
const [events, setEvents] = useState<CalendarEvent[]>([])
@@ -15,7 +16,6 @@ export default function HomePage() {
const [title, setTitle] = useState('')
const [start, setStart] = useState('')
// Load events only in the browser
useEffect(() => {
(async () => {
const stored = await getAllEvents()
@@ -42,11 +42,61 @@ export default function HomePage() {
setEvents([])
}
// --- IMPORT ---
const handleImport = async (file: File) => {
const text = await file.text()
const parsed = parseICS(text)
// Save to DB and update state
for (const ev of parsed) {
await addEvent(ev)
}
const stored = await getAllEvents()
setEvents(stored)
}
// --- EXPORT ---
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 = 'events.ics'
document.body.appendChild(a)
a.click()
document.body.removeChild(a)
URL.revokeObjectURL(url)
}
return (
<div>
<div className="flex gap-2">
<div className="space-y-4">
<div className="flex gap-2 flex-wrap">
<Button onClick={() => setOpen(true)}>Add Event</Button>
{events.length > 0 && <Button variant="destructive" onClick={handleClearAll}>Clear All</Button>}
{events.length > 0 && (
<>
<Button variant="secondary" onClick={handleExport}>
Export .ics
</Button>
<Button variant="destructive" onClick={handleClearAll}>
Clear All
</Button>
</>
)}
<label className="cursor-pointer">
<span className="px-3 py-2 bg-blue-500 text-white rounded">Import .ics</span>
<input
type="file"
accept=".ics"
hidden
onChange={e => {
if (e.target.files?.length) {
handleImport(e.target.files[0])
}
}}
/>
</label>
</div>
<ul className="mt-4 space-y-2">