/// import { Cell, computed, Default, ifElse, lift, NAME, navigateTo, pattern, UI, } from "commontools"; import EventDetail from "./event-detail.tsx"; interface Event { title: string; date: string; time: Default; notes: Default; } interface Input { events: Cell>; } interface Output { events: Event[]; todayDate: string; } const getTodayDate = (): string => { const now = new Date(); return now.toISOString().split("T")[0]; }; const formatDate = lift((date: string): string => { if (!date) return ""; const d = new Date(date + "T00:00:00"); return d.toLocaleDateString("en-US", { weekday: "short", month: "short", day: "numeric", }); }); const isToday = lift((date: string): boolean => { return date === getTodayDate(); }); const isPast = lift((date: string): boolean => { return date < getTodayDate(); }); const groupEventsByDate = lift((events: Event[]): Record => { if (!Array.isArray(events)) return {}; const sorted = [...events].sort((a, b) => { if (a.date !== b.date) return a.date.localeCompare(b.date); return (a.time || "").localeCompare(b.time || ""); }); const groups: Record = {}; for (const event of sorted) { if (!groups[event.date]) groups[event.date] = []; groups[event.date].push(event); } return groups; }); const getSortedDates = lift((grouped: Record): string[] => { return Object.keys(grouped).sort(); }); export default pattern(({ events }) => { const todayDate = getTodayDate(); const newTitle = Cell.of(""); const newDate = Cell.of(todayDate); const newTime = Cell.of(""); const eventCount = computed(() => events.get().length); const grouped = groupEventsByDate(events); const dates = getSortedDates(grouped); return { [NAME]: "Calendar", [UI]: ( Calendar ({eventCount}) {todayDate} {dates.map((date) => { const dateEvents = lift(( args: { g: Record; d: string }, ) => args.g[args.d] || [])({ g: grouped, d: date }); const dateIsToday = isToday(date); const dateIsPast = isPast(date); return ( {formatDate(date)} {ifElse( dateIsToday, Today , null, )} {dateEvents.map((event) => ( { const detail = EventDetail({ event }); return navigateTo(detail); }} > {event.time && ( {event.time} )} {event.title || "(untitled)"} { const current = events.get(); const idx = current.findIndex((e) => Cell.equals(event, e) ); if (idx >= 0) { events.set(current.toSpliced(idx, 1)); } }} > × ))} ); })} {ifElse( computed(() => events.get().length === 0),
No events yet. Add one below!
, null, )}
{ const title = newTitle.get().trim(); const date = newDate.get(); if (title && date) { events.push({ title, date, time: newTime.get(), notes: "", }); newTitle.set(""); newTime.set(""); } }} > Add
), events, todayDate, }; });