Calendars as entities, uniform widget contract, multi-dashboard tasks

Architecture refinements before module work begins:

- Calendars become first-class entities (mirror of lists). Each
  calendar has its own visibility (private | household) and is
  independently shareable. Updated CLAUDE.md data model and task 10.

- Drop the singleton/parameterized split for dashboard widgets. Every
  widget declares a configSchema + defaultConfig + resolveConfigOptions
  and every placement is an independent instance — same widgetId can
  appear multiple times on one dashboard pointed at different things.
  Updated task 04 contract; tasks 11/12 widget sections aligned.

- Add tasks 25 (multiple dashboards per user) and 26 (customizable
  layout + widget configuration). Replaces the previously-considered
  three-task plan with a cleaner two-task split that the uniform
  contract enables. Phase 3 index updated; STATUS records the rationale.

- Task 20 reframed as a single-dashboard MVP that exercises the new
  contract end-to-end before the customization layer lands.
This commit is contained in:
ginnoir
2026-05-06 01:14:49 -05:00
parent 35a14b81c9
commit 09d6adbf18
10 changed files with 370 additions and 64 deletions
+6 -4
View File
@@ -60,7 +60,7 @@ src/
### Core primitives every module gets
- **Entity registry.** Modules declare entity types; share-link, activity log, search, reminders all work against any registered entity.
- **Dashboard contribution API.** Each module exports a widget + priority. Dashboard composes whatever's installed.
- **Dashboard widget registry.** Every widget is uniformly configurable (no singleton/parameterized split) and reusable — each placement on a dashboard is an independent instance with its own config. Each user has multiple dashboards; the active dashboard composes whatever widgets they've placed.
- **Quick-add registry.** Modules register quick actions for the dashboard's `+` menu.
- **Share-link service.** `createShareLink(entityType, entityId, { expiresAt, capabilities })``fam.ginnoir.com/s/<token>`. Generic.
- **Notification bus.** `notify(userId, { title, body, url })` fans out to web push + in-app + (optional) ntfy.
@@ -74,15 +74,17 @@ src/
## Data model (v1)
- `users`, `households`, `household_members`
- `calendar_events` — title, start, end, all_day, location, notes, color, owner. `rrule` text column reserved (no recurrence in v1). `external_source`/`external_id` nullable for future Google/Apple sync.
- `lists` (type enum: `shopping` | `task` | future), `list_items`
- `calendars` — name, color, owner_id, visibility (`private` | `household`). First-class entity; users create as many as they want, each independently shareable via the share-link service.
- `calendar_events``calendar_id` fk, title, start, end, all_day, location, notes, owner. `rrule` text column reserved (no recurrence in v1). `external_source`/`external_id` nullable for future Google/Apple sync.
- `lists` (type text — not enum, to allow extension), `list_items`
- `notes` — title, body, pinned, remind_at
- `dashboards` — per-user named dashboards (any user can have many), `layout` jsonb of placed widgets `[{ widgetId, config, x, y, w, h }]`.
- `share_links` — entity_type, entity_id, token, capabilities jsonb, expires_at
- `activity_log` — entity_type, entity_id, actor, action, payload jsonb
- `push_subscriptions`
- `reminders` — entity_type, entity_id, fire_at, channel. Generic; used by notes/events/anything.
All entity tables include `household_id`. All mutations are scoped to the caller's household.
All household-scoped entity tables include `household_id`. Sub-entities (`calendar_events`, `list_items`) inherit scope via their parent. All reads/mutations are gated by the caller's household membership and, where relevant, per-entity visibility (e.g. private calendars).
---