Files
ginnoir 09d6adbf18 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.
2026-05-06 01:14:49 -05:00

83 lines
2.8 KiB
Markdown

# 20 — Dashboard composition (single-dashboard MVP)
## Goal
Each user sees a single default dashboard with widgets pre-seeded from the registry. Every widget is rendered with a `config` (per the task 04 contract). No customization yet — task 25/26 add multiple dashboards and the editor.
## Why
Lands a working dashboard quickly so the app is usable while the customization layer is built. Validates the configurable widget contract end-to-end. The schema introduced here (one row per user) gets generalized in task 25 to support many dashboards per user.
## Depends on
- 10 (calendar), 11 (lists), 12 (notes)
## Scope
### Schema (interim, generalized in task 25)
Add a `default_dashboard_layout` jsonb column on `users`. Shape:
```jsonc
{
"version": 1,
"widgets": [
{
"widgetId": "calendar.upcoming",
"config": { "calendarIds": "all", "days": 3 },
"x": 0,
"y": 0,
"w": 6,
"h": 3,
},
{
"widgetId": "lists.list",
"config": { "listIds": "all", "showCompleted": false },
"x": 6,
"y": 0,
"w": 6,
"h": 3,
},
],
}
```
If null, fall back to the registry's default seed (sorted by `defaultPriority`, packed by `defaultSize`).
### Layout
- 12-col responsive grid using basic CSS Grid (no drag library yet — task 26 introduces it).
- Mobile: single column ordered by `y` then `x`.
- Each widget is a server component that receives `config` and renders inside a card. Suspense boundary per widget so they load in parallel.
### Seeded widgets the registry must contribute
These are widget definitions, not hardcoded slots:
- `calendar.upcoming` (calendar module)
- `calendar.month` (calendar module)
- `lists.list` (lists module)
- `notes.filtered` (notes module)
- `core.activity` (\_core; depends on task 22 — until then render a placeholder card)
### Header
- Page title "Dashboard".
- "+" floating action button opens the quick-add menu (task 21).
- _No_ edit-mode toggle yet — that lands in task 26.
## Out of scope
- Multiple dashboards (task 25).
- Drag, resize, picker, configure UI (task 26).
- User-configurable widget order. Defaults are taken from `defaultPriority`.
## Acceptance criteria
- [ ] First-time user lands on a populated dashboard with widgets seeded from the registry's `defaultPriority` + `defaultSize`.
- [ ] Every widget receives a typed `config` and renders without `if (widgetId === ...)` branches in the dashboard code.
- [ ] Removing a module from `src/modules/index.ts` cleanly removes its widgets — no errors, no blank slot.
- [ ] Widgets load in parallel (verify in network tab — no waterfall).
- [ ] Mobile (375px) is usable without horizontal scroll.
- [ ] The schema column added here has a clean migration path to the `dashboards` table introduced in task 25 (a one-line copy-into-default-dashboard migration is enough).