- Three test cases: event rendering with color dots, no-events empty state, no-sources empty state
- Mocks calendar-api and next-intl following existing test patterns
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- SearchWidget: provider dropdown, text input, button; opens search in new tab via window.open (D-14/D-15)
- NoteWidget: MDEditor with compact toolbar, debounced autosave (1500ms), AbortController for in-flight cancellation (D-16/D-17/D-18)
- rehype-sanitize enabled for Markdown XSS prevention (T-05-05)
- Widget registry updated with wireSearchWidget/wireNoteWidget (no more placeholders)
- dashboard-api.ts: added fetchSearchProviders, addSearchProvider, removeSearchProvider, signal support on updateWidgetConfig
- 9 new tests passing (search: 5, note: 4)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Install react-grid-layout@2.2.3 and react-resizable
- Create widget-registry.tsx with all 4 widget types, WIDGET_CONSTRAINTS, WidgetProps
- Create dashboard-api.ts with fetch/save layout and widget CRUD functions
- Create dashboard-store.ts (Zustand, NO persist — D-05) with edit mode and auto-save on exit
- Create DashboardGrid with react-grid-layout v2 Responsive, ResizeObserver width
- Create ClockWidget using Intl.DateTimeFormat (no manual UTC offsets)
- Create WidgetWrapper with drag handle and delete button in edit mode
- Create EditModeToggle (pencil/checkmark), WidgetCatalogModal (2x2 grid)
- Rewrite portal page.tsx as dashboard with grid, edit toggle, widget catalog
- Add ResizeObserver polyfill in test setup, CSS mock support in vitest config
- All 5 tests green (dashboard grid + clock widget)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>