Files

135 lines
6.8 KiB
Markdown

---
phase: 05-dashboard-calendar
plan: 01
subsystem: dashboard
tags: [dashboard, widgets, grid, settings, prisma, nestjs, react-grid-layout]
dependency_graph:
requires: []
provides: [DashboardModule, DashboardGrid, ClockWidget, WidgetRegistry, SettingsLayout]
affects: [apps/api/prisma/schema.prisma, apps/api/src/app.module.ts, apps/web/src/app/(portal)/page.tsx, apps/web/src/components/layout/header.tsx]
tech_stack:
added: [react-grid-layout@2.2.3, react-resizable]
patterns: [Zustand store without persist, react-grid-layout v2 Responsive API, widget-registry pattern, dragConfig/resizeConfig v2 API]
key_files:
created:
- apps/api/src/dashboard/dashboard.module.ts
- apps/api/src/dashboard/dashboard.controller.ts
- apps/api/src/dashboard/dashboard.service.ts
- apps/api/src/dashboard/dto/save-layout.dto.ts
- apps/api/src/dashboard/dto/create-widget.dto.ts
- apps/api/src/dashboard/dto/update-widget-config.dto.ts
- apps/web/src/components/dashboard/dashboard-grid.tsx
- apps/web/src/components/dashboard/edit-mode-toggle.tsx
- apps/web/src/components/dashboard/widget-catalog-modal.tsx
- apps/web/src/components/dashboard/widget-registry.tsx
- apps/web/src/components/dashboard/widgets/clock-widget.tsx
- apps/web/src/components/dashboard/widgets/widget-wrapper.tsx
- apps/web/src/lib/dashboard-api.ts
- apps/web/src/lib/stores/dashboard-store.ts
- apps/web/src/app/(portal)/settings/layout.tsx
- apps/web/src/app/(portal)/settings/page.tsx
- apps/web/src/components/settings/settings-sidebar.tsx
- apps/web/src/components/dashboard/dashboard-grid.test.tsx
- apps/web/src/components/dashboard/widgets/clock-widget.test.tsx
modified:
- apps/api/prisma/schema.prisma
- apps/api/src/app.module.ts
- apps/web/package.json
- apps/web/src/app/(portal)/page.tsx
- apps/web/src/components/layout/header.tsx
- apps/web/src/messages/de.json
- apps/web/src/messages/en.json
- apps/web/vitest.config.ts
- apps/web/src/test/setup.ts
decisions:
- "react-grid-layout v2 uses dragConfig/resizeConfig instead of isDraggable/isResizable"
- "react-resizable installed as direct dependency (pnpm strict isolation requires it)"
- "widget-registry.tsx uses wireClockWidget() deferred wiring to avoid circular deps"
- "ResizeObserver polyfill added to test setup for jsdom compatibility"
- "Prisma JSON fields cast via unknown to InputJsonValue for type safety"
metrics:
duration: 14min
completed: 2026-06-24T09:25:00Z
tasks: 4
files_created: 19
files_modified: 9
---
# Phase 05 Plan 01: Dashboard Grid + Clock Widget + Settings Shell Summary
Full vertical stack for dashboard: Prisma models (DashboardLayout, WidgetInstance), NestJS CRUD API with userId ownership verification, react-grid-layout v2 Responsive grid with 4-widget registry, clock widget using Intl.DateTimeFormat, Zustand store without persist (D-05), settings page shell with sub-sidebar, and all phase i18n keys.
## Tasks Completed
| Task | Name | Commit | Key Files |
|------|------|--------|-----------|
| 1 | Dashboard backend | 950eebb | schema.prisma, dashboard.controller.ts, dashboard.service.ts |
| 2 | Dashboard grid + clock widget + store | d5e1c42 | dashboard-grid.tsx, clock-widget.tsx, widget-registry.tsx, dashboard-store.ts |
| 2 (RED) | Failing tests | 792b766 | dashboard-grid.test.tsx, clock-widget.test.tsx |
| 3 | Settings shell + header link + i18n | 8a9c1ae | settings/layout.tsx, settings-sidebar.tsx, de.json, en.json |
| 4 | Prisma schema push | (no code change) | Database tables created |
## Verification Results
- `prisma validate`: PASS
- `tsc --noEmit` (apps/api): PASS
- `tsc --noEmit` (apps/web): PASS
- `vitest run src/components/dashboard`: 5/5 PASS
- `vitest run` (all tests): 31/31 PASS
- JSON parse (de.json, en.json): PASS
- `prisma db push`: "database is already in sync"
## Deviations from Plan
### Auto-fixed Issues
**1. [Rule 3 - Blocking] react-grid-layout v2 API changes**
- **Found during:** Task 2
- **Issue:** react-grid-layout v2 uses `dragConfig`/`resizeConfig` objects instead of `isDraggable`/`isResizable` props. The type `Layouts` is renamed to `ResponsiveLayouts`.
- **Fix:** Rewrote grid component to use v2 API: `dragConfig: { enabled, handle }` and `resizeConfig: { enabled }`.
- **Files modified:** apps/web/src/components/dashboard/dashboard-grid.tsx
**2. [Rule 3 - Blocking] pnpm strict isolation for react-resizable CSS**
- **Found during:** Task 2
- **Issue:** `react-resizable/css/styles.css` import failed in tests because react-resizable is a transitive dep of react-grid-layout, not directly installed. pnpm strict isolation blocks resolution.
- **Fix:** Added `react-resizable` as direct dependency in apps/web/package.json.
- **Files modified:** apps/web/package.json
**3. [Rule 3 - Blocking] ResizeObserver not available in jsdom**
- **Found during:** Task 2
- **Issue:** DashboardGrid uses ResizeObserver for container width measurement, which jsdom doesn't provide.
- **Fix:** Added ResizeObserver polyfill to apps/web/src/test/setup.ts that fires immediately with mock dimensions.
- **Files modified:** apps/web/src/test/setup.ts
**4. [Rule 3 - Blocking] widget-registry.ts JSX in .ts file**
- **Found during:** Task 2
- **Issue:** PlaceholderWidget component uses JSX but file was `.ts` extension.
- **Fix:** Renamed to `widget-registry.tsx`.
- **Files modified:** apps/web/src/components/dashboard/widget-registry.tsx
**5. [Rule 1 - Bug] Prisma JSON type mismatch**
- **Found during:** Task 1
- **Issue:** `Record<string, unknown>` not assignable to Prisma's `InputJsonValue` type.
- **Fix:** Cast JSON fields via `as unknown as Prisma.InputJsonValue`.
- **Files modified:** apps/api/src/dashboard/dashboard.service.ts
## Decisions Made
| Decision | Context | Outcome |
|----------|---------|---------|
| react-grid-layout v2 Responsive API | v2 is a complete TypeScript rewrite with different prop names | Used `dragConfig`/`resizeConfig` objects instead of boolean props |
| react-resizable as direct dep | pnpm strict isolation prevents resolving transitive CSS imports | Added as explicit dependency in apps/web |
| wireClockWidget() pattern | Avoid circular deps between widget-registry and clock-widget | Deferred wiring function called from page.tsx at mount time |
| Prisma db push via docker exec | DB port not exposed to host | Copied schema into API container and ran prisma db push from there |
## Known Stubs
| File | Line | Stub | Reason |
|------|------|------|--------|
| widget-registry.tsx | search/calendar/note entries | PlaceholderWidget component | Implemented in plans 05-02 (search/note) and 05-03 (calendar) |
| settings-sidebar.tsx | /settings/dashboard, /settings/dashboard/calendar links | Links to pages not yet created | Created in plans 05-02 and 05-03 |
## Self-Check: PASSED
All 19 created files verified present. All 4 commit hashes found in git log.