Add general calendar events (recording, social, task, general); nav links; v1.1.16

- Four event types on the calendar: General, Recording, Social, Task — each color-coded
- + Event button opens form with type picker, title, date, notes, reminder
- Task events have a Mark Complete toggle; completed events show strikethrough
- Edit/delete popover for each event
- Email reminders work for calendar events same as episodes
- Added Email/Calendar nav links to /contacts header
- Added Email link to /calendar header (already had Admin)
- /email already links to Contacts and Calendar

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
nmemmert
2026-07-28 11:26:48 -04:00
parent de201ff356
commit f0635753f1
10 changed files with 469 additions and 44 deletions
+31
View File
@@ -31,6 +31,7 @@ import {
EPISODE_PLAYS_FILE,
ANALYTICS_EVENTS_FILE,
EMAIL_SETTINGS_FILE,
CALENDAR_EVENTS_FILE,
EMPTY_HIT_STATS,
EMPTY_VISITOR_STATS,
DEFAULT_REPLY_TEMPLATES,
@@ -416,6 +417,36 @@ export function loadEmailSettingsFromDisk() {
})
}
// ── Calendar events ────────────────────────────────────────────────────────
export function queueCalendarEventsWrite() {
state.calendarEventsWritePromise = state.calendarEventsWritePromise
.then(async () => {
await mkdir(DATA_DIR, { recursive: true })
await writeFile(
CALENDAR_EVENTS_FILE,
JSON.stringify({ events: state.calendarEvents, updatedAt: new Date().toISOString() }, null, 2),
'utf8',
)
})
.catch(err => {
console.error('[calendar-events] failed to write:', err)
})
}
export function loadCalendarEventsFromDisk() {
return readFile(CALENDAR_EVENTS_FILE, 'utf8')
.then(raw => {
const parsed = JSON.parse(raw)
if (Array.isArray(parsed?.events)) {
state.calendarEvents = parsed.events
}
})
.catch(() => {
// No file yet — start with empty array
})
}
// ── Podcast checklist ──────────────────────────────────────────────────────
export function queuePodcastChecklistWrite() {