--- phase: 05-dashboard-calendar plan: 04 subsystem: ui tags: [calendar, frontend, widget, settings, react, next-intl, tdd, caldav, ics, exchange] requires: - phase: 05-dashboard-calendar/01 provides: Dashboard grid + widget registry + Prisma models + settings layout + i18n - phase: 05-dashboard-calendar/02 provides: Widget settings panel pattern + SearchProvider CRUD pattern - phase: 05-dashboard-calendar/03 provides: Calendar backend API (source CRUD, event aggregation, encryption, providers) provides: - CalendarWidget component rendering upcoming events with source color dots - CalendarSettingsPanel for managing calendar sources with visibility toggle - CalendarSourceForm with type-specific fields and https-only validation - calendar-api.ts client (fetchSources/addSource/updateSource/deleteSource/testSource/fetchEvents) - Widget registry wired with real CalendarWidget (replaces placeholder) affects: [05-dashboard-calendar/05] tech-stack: added: [] patterns: [calendar-api-client-pattern, visibility-toggle-optimistic-update, source-form-type-conditional-fields, tdd-red-green-commit-flow] key-files: created: - apps/web/src/lib/calendar-api.ts - apps/web/src/components/dashboard/widgets/calendar-widget.tsx - apps/web/src/components/dashboard/widgets/calendar-widget.test.tsx - apps/web/src/components/settings/calendar-settings-panel.tsx - apps/web/src/components/settings/calendar-source-form.tsx - apps/web/src/app/(portal)/settings/dashboard/calendar/page.tsx - apps/web/src/app/(portal)/settings/dashboard/calendar/calendar-settings.test.tsx modified: - apps/web/src/components/dashboard/widget-registry.tsx - apps/web/src/app/(portal)/page.tsx - apps/web/src/messages/de.json - apps/web/src/messages/en.json key-decisions: - "Optimistic UI update for visibility toggle — reverts on API error" - "Auto-run testSource after adding a new source (connection test feedback)" - "CalendarSourceForm uses URL constructor for client-side https validation (T-05-14)" patterns-established: - "Calendar API client: separate module from dashboard-api following same credentials:'include' pattern" - "Optimistic toggle: update local state immediately, revert on error for responsive UX" - "Type-conditional form fields: Exchange-mode select only for exchange type, hide credentials for ICS" requirements-completed: [DASH-05, CAL-02, CAL-03] duration: 5min completed: 2026-06-24 --- # Phase 05 Plan 04: Calendar Frontend Summary **Calendar widget displaying aggregated events with source color dots, plus settings page for CalDAV/ICS/Exchange source management with visibility toggle and connection testing** ## Performance - **Duration:** 5 min - **Started:** 2026-06-24T13:21:01Z - **Completed:** 2026-06-24T13:26:32Z - **Tasks:** 2 - **Files modified:** 11 ## Accomplishments - Calendar widget fetches and displays upcoming events from visible sources via `/api/calendar/events` with 5-min refresh cycle - Three distinct empty states per UI-SPEC: no sources, no events, loading - Source management settings page at `/settings/dashboard/calendar` with visibility toggle (CAL-02), type badges, color dots, connection status indicators - Calendar source form with conditional fields: Exchange-mode select for Exchange type, hidden credentials for ICS, 8-color palette picker - Delete confirmation dialog per UI-SPEC destructive pattern - Client-side https-only URL validation (T-05-14 threat mitigation) - All 6 tests passing with TDD RED/GREEN commit flow ## Task Commits Each task was committed atomically: 1. **Task 1: Calendar API client + calendar widget (RED)** - `2d8699e` (test) 2. **Task 1: Calendar API client + calendar widget (GREEN)** - `c0f2f4c` (feat) 3. **Task 2: Calendar settings page (RED)** - `f99ff9a` (test) 4. **Task 2: Calendar settings page (GREEN)** - `24de136` (feat) ## TDD Gate Compliance - RED gate: `2d8699e` (test commit) exists before `c0f2f4c` (feat commit) for Task 1 - RED gate: `f99ff9a` (test commit) exists before `24de136` (feat commit) for Task 2 - Both tasks follow proper RED -> GREEN sequence ## Files Created/Modified - `apps/web/src/lib/calendar-api.ts` - Calendar API client with typed interfaces mirroring 05-03 backend contract - `apps/web/src/components/dashboard/widgets/calendar-widget.tsx` - Upcoming events list widget with color dots and three empty states - `apps/web/src/components/dashboard/widgets/calendar-widget.test.tsx` - 3 tests: events rendering, no-events, no-sources - `apps/web/src/components/settings/calendar-settings-panel.tsx` - Source list with visibility toggle, edit/delete, connection status - `apps/web/src/components/settings/calendar-source-form.tsx` - Add/edit form with type-conditional fields and https validation - `apps/web/src/app/(portal)/settings/dashboard/calendar/page.tsx` - Route page for calendar settings - `apps/web/src/app/(portal)/settings/dashboard/calendar/calendar-settings.test.tsx` - 3 tests: source list, visibility toggle, form validation - `apps/web/src/components/dashboard/widget-registry.tsx` - Added wireCalendarWidget() function - `apps/web/src/app/(portal)/page.tsx` - Wired CalendarWidget into registry - `apps/web/src/messages/de.json` - Added calendar.loading i18n key - `apps/web/src/messages/en.json` - Added calendar.loading i18n key ## Decisions Made - **Optimistic UI for visibility toggle:** Local state updated immediately on click, reverted on API error. Provides responsive UX without waiting for network roundtrip. - **Auto-run connection test after source creation:** After addSource succeeds, testSource is called automatically and result displayed via connection status indicator (green check / error icon). - **URL validation via URL constructor:** Uses `new URL()` with protocol check rather than regex for robust https-only client-side validation (T-05-14). Server-side DTO validation remains authoritative. ## Deviations from Plan ### Auto-fixed Issues **1. [Rule 2 - Missing Critical] Added calendar.loading i18n key** - **Found during:** Task 1 - **Issue:** Widget uses `t('calendar.loading')` for loading state but key was missing from i18n files - **Fix:** Added `calendar.loading` to both de.json and en.json - **Files modified:** de.json, en.json - **Committed in:** c0f2f4c (Task 1 commit) --- **Total deviations:** 1 auto-fixed (1 missing critical) **Impact on plan:** Minor i18n key addition required for correctness. No scope creep. ## Issues Encountered None - all components implemented and tested without blocking issues. ## Threat Mitigations Implemented | Threat ID | Mitigation | |-----------|------------| | T-05-14 | Client-side https-only URL validation via URL constructor before form submit | | T-05-15 | CalendarWidget only consumes /api/calendar/events; no password fields in frontend types | ## User Setup Required None - no external service configuration required. Calendar backend from 05-03 must be running with CALENDAR_ENCRYPTION_KEY configured. ## Next Phase Readiness - Calendar frontend complete: widget + settings page fully wired to 05-03 backend - Ready for Plan 05 (E2E testing / integration verification) - Widget registry now has all four widget types wired with real components ## Self-Check: PASSED - All 7 created files verified present on disk - All 4 task commits verified in git log (2d8699e, c0f2f4c, f99ff9a, 24de136) --- *Phase: 05-dashboard-calendar* *Completed: 2026-06-24*