# 10 — Calendar module ## Goal Implement the `calendar` module: shared events with month/week/day views and CRUD. ## Depends on - 04 (module loader), 07 (household) ## Scope ### Schema (`src/modules/calendar/schema.ts`) - `calendar_events`: - `id` uuid pk - `household_id` fk - `title` text - `start_at` timestamptz, `end_at` timestamptz - `all_day` boolean - `location` text nullable - `notes` text nullable - `color` text nullable - `owner_id` fk users - `rrule` text nullable (reserved; no UI) - `external_source` text nullable, `external_id` text nullable (reserved) - `created_at`, `updated_at` ### Server (`src/modules/calendar/server/`) - `listEvents({ from, to })` — household-scoped, range query. - `createEvent`, `updateEvent`, `deleteEvent` — server actions, validate with Zod. ### UI (`src/modules/calendar/components/`) - `/calendar` page with month / week / day toggle. Recommended lib: **FullCalendar** (`@fullcalendar/react` + day/week/month plugins) — handles the heavy lifting; we own only the data layer. - Click a day cell → create-event dialog. Click an event → edit dialog. Drag-resize updates `end_at`. ### Manifest - Registers entity type `calendar.event` with `share` enabled (read-only by default), `reminder` enabled, `search` enabled (title + notes + location). - Registers nav entry `/calendar`. - Dashboard widget: next 3 days (built in task 20, but expose the data fetcher here). - Quick-add: "New event" → opens create dialog. ## Out of scope - Recurrence UI (rrule column reserved, ignored on read). - External calendar sync. - Free/busy aggregation across users. ## Acceptance criteria - [ ] CRUD works end-to-end with optimistic updates. - [ ] All queries scoped to `household_id`; cross-household leakage is impossible (verify with a second seeded household in a test). - [ ] Manifest registers all four capabilities (entity, nav, widget data, quick-add). - [ ] One Playwright happy-path test: create → see on calendar → edit → delete.