feat(calendar): internal event store with FullCalendar UI and CalDAV push sync

- AI calendar tools now always available (moved from _CALDAV_TOOLS to _CORE_TOOLS);
  create/list/search/update/delete events go through the internal DB store first,
  with fire-and-forget CalDAV push sync when the user has CalDAV configured
- Add EventEntry interface and typed API helpers (listEvents, createEvent,
  getEvent, updateEvent, deleteEvent) to client.ts
- Install @fullcalendar/vue3, daygrid, timegrid, interaction, core packages
- Add EventSlideOver.vue: create/edit/delete slide-over with title, start/end,
  all-day toggle, location, description, color picker, and project selector
- Add CalendarView.vue: month/week/day FullCalendar with drag-drop and resize
  wired to PATCH /api/events/:id; click empty date opens create slide-over
- Wire /calendar route, Calendar nav link in AppHeader, g+l keyboard shortcut

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-25 13:38:09 -04:00
parent 8d330afc6d
commit c3e201d26a
9 changed files with 970 additions and 52 deletions
+2
View File
@@ -77,6 +77,7 @@ router.afterEach(() => {
<router-link to="/tasks" class="nav-link">Tasks</router-link>
<router-link to="/chat" :class="['nav-link', { 'router-link-active': isChatActive }]">Chat</router-link>
<router-link to="/graph" class="nav-link">Graph</router-link>
<router-link to="/calendar" class="nav-link">Calendar</router-link>
<router-link to="/briefing" class="nav-link">Briefing</router-link>
<router-link to="/shared" class="nav-link">Shared</router-link>
</div>
@@ -126,6 +127,7 @@ router.afterEach(() => {
<router-link to="/tasks" class="nav-link">Tasks</router-link>
<router-link to="/chat" :class="['nav-link', { 'router-link-active': isChatActive }]">Chat</router-link>
<router-link to="/graph" class="nav-link">Graph</router-link>
<router-link to="/calendar" class="nav-link">Calendar</router-link>
<router-link to="/briefing" class="nav-link">Briefing</router-link>
<router-link to="/shared" class="nav-link">Shared</router-link>
<div class="mobile-divider"></div>