- Create Toaster component wrapping sonner at bottom-right position - Add showToast utility with success/error/info methods - Error toasts persist until dismissed, others auto-dismiss after 5s - Migrate error handling to toasts across all pages: - Dashboard (override toggle errors) - Settings (save/load success/error) - Garmin settings (connection success/error) - Calendar (load errors) - Period History (load/delete errors) - Add dark mode support for toast styling - Add Toaster provider to root layout - 27 new tests (23 toaster component + 4 integration) - Total: 977 unit tests passing P5.2 COMPLETE - All P0-P5 items now complete. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1136 lines
70 KiB
Markdown
1136 lines
70 KiB
Markdown
# PhaseFlow Implementation Plan
|
|
|
|
This file is maintained by Ralph. Run `./ralph-sandbox.sh plan 3` to generate tasks.
|
|
|
|
## Current State Summary
|
|
|
|
### Overall Status: 977 unit tests passing across 50 test files + 64 E2E tests across 6 files
|
|
|
|
### Library Implementation
|
|
| File | Status | Gap Analysis |
|
|
|------|--------|--------------|
|
|
| `cycle.ts` | **COMPLETE** | 22 tests covering all functions including dynamic phase boundaries for variable cycle lengths |
|
|
| `nutrition.ts` | **COMPLETE** | 17 tests covering getNutritionGuidance, getSeedSwitchAlert, phase-specific carb ranges, keto guidance |
|
|
| `email.ts` | **COMPLETE** | 30 tests covering sendDailyEmail, sendPeriodConfirmationEmail, sendTokenExpirationWarning, email formatting, subject lines, structured logging |
|
|
| `ics.ts` | **COMPLETE** | 33 tests covering generateIcsFeed (90 days of phase events), ICS format validation, timezone handling, period prediction feedback, CATEGORIES for calendar colors |
|
|
| `encryption.ts` | **COMPLETE** | 14 tests covering AES-256-GCM encrypt/decrypt round-trip, error handling, key validation |
|
|
| `decision-engine.ts` | **COMPLETE** | 8 priority rules + override handling with `getDecisionWithOverrides()`, 24 tests |
|
|
| `garmin.ts` | **COMPLETE** | 33 tests covering fetchGarminData, fetchHrvStatus, fetchBodyBattery, fetchIntensityMinutes, isTokenExpired, daysUntilExpiry, error handling, token validation |
|
|
| `pocketbase.ts` | **COMPLETE** | 9 tests covering `createPocketBaseClient()`, `isAuthenticated()`, `getCurrentUser()`, `loadAuthFromCookies()` |
|
|
| `auth-middleware.ts` | **COMPLETE** | 9 tests covering `withAuth()` wrapper for API route protection, structured logging for auth failures |
|
|
| `middleware.ts` (Next.js) | **COMPLETE** | 12 tests covering page protection, redirects to login |
|
|
| `logger.ts` | **COMPLETE** | 16 tests covering JSON output, log levels, error stack traces, child loggers |
|
|
| `metrics.ts` | **COMPLETE** | 33 tests covering metrics collection, counters, gauges, histograms, Prometheus format |
|
|
|
|
### Infrastructure Gaps (from specs/ - pending implementation)
|
|
| Gap | Spec Reference | Task | Priority |
|
|
|-----|----------------|------|----------|
|
|
| Health Check Endpoint | specs/observability.md | P2.15 | **COMPLETE** |
|
|
| Prometheus Metrics | specs/observability.md | P2.16 | **COMPLETE** |
|
|
| Structured Logging (pino) | specs/observability.md | P2.17 | **COMPLETE** |
|
|
| OIDC Authentication | specs/authentication.md | P2.18 | **COMPLETE** |
|
|
| Token Expiration Warnings | specs/email.md | P3.9 | **COMPLETE** |
|
|
|
|
### API Routes (18 route files, 21 HTTP endpoints)
|
|
| Route | Status | Notes |
|
|
|-------|--------|-------|
|
|
| POST /api/auth/logout | **COMPLETE** | Clears pb_auth cookie, logs out user (5 tests) |
|
|
| GET /api/user | **COMPLETE** | Returns user profile with `withAuth()` |
|
|
| PATCH /api/user | **COMPLETE** | Updates cycleLength, notificationTime, timezone (17 tests) |
|
|
| POST /api/cycle/period | **COMPLETE** | Logs period start, updates user, creates PeriodLog with prediction tracking (13 tests) |
|
|
| GET /api/cycle/current | **COMPLETE** | Returns cycle day, phase, config, daysUntilNextPhase (10 tests) |
|
|
| GET /api/today | **COMPLETE** | Returns decision, cycle, biometrics, nutrition (22 tests) |
|
|
| POST /api/overrides | **COMPLETE** | Adds override to user.activeOverrides (14 tests) |
|
|
| DELETE /api/overrides | **COMPLETE** | Removes override from user.activeOverrides (14 tests) |
|
|
| POST /api/garmin/tokens | **COMPLETE** | Stores encrypted Garmin OAuth tokens (15 tests) |
|
|
| DELETE /api/garmin/tokens | **COMPLETE** | Clears tokens and disconnects Garmin (15 tests) |
|
|
| GET /api/garmin/status | **COMPLETE** | Returns connection status, expiry, warning level (11 tests) |
|
|
| GET /api/calendar/[userId]/[token].ics | **COMPLETE** | Token validation, ICS generation with period prediction feedback, caching headers (11 tests) |
|
|
| POST /api/calendar/regenerate-token | **COMPLETE** | Generates 32-char token, returns URL (9 tests) |
|
|
| POST /api/cron/garmin-sync | **COMPLETE** | Syncs Garmin data for all users, creates DailyLogs, sends token expiration warnings (32 tests) |
|
|
| POST /api/cron/notifications | **COMPLETE** | Sends daily emails with timezone matching, DailyLog handling (20 tests) |
|
|
| GET /api/history | **COMPLETE** | Paginated historical daily logs with date filtering (19 tests) |
|
|
| GET /api/period-history | **COMPLETE** | Paginated period logs with cycle lengths and average (18 tests) |
|
|
| PATCH /api/period-logs/[id] | **COMPLETE** | Edit period log startDate (10 tests) |
|
|
| DELETE /api/period-logs/[id] | **COMPLETE** | Delete period log with user.lastPeriodDate update (6 tests) |
|
|
| GET /api/health | **COMPLETE** | Health check for deployment monitoring (14 tests) |
|
|
| GET /metrics | **COMPLETE** | 33 tests (18 lib + 15 route) |
|
|
|
|
### Pages (8 total)
|
|
| Page | Status | Notes |
|
|
|------|--------|-------|
|
|
| Dashboard (`/`) | **COMPLETE** | Wired with /api/today, DecisionCard, DataPanel, NutritionPanel, OverrideToggles |
|
|
| Login (`/login`) | **COMPLETE** | Email/password form with auth, error handling, loading states, rate limiting |
|
|
| Settings (`/settings`) | **COMPLETE** | Form with cycleLength, notificationTime, timezone |
|
|
| Settings/Garmin (`/settings/garmin`) | **COMPLETE** | Token management UI, connection status, disconnect functionality, 27 tests |
|
|
| Calendar (`/calendar`) | **COMPLETE** | MonthView with navigation, ICS subscription section, token regeneration, 23 tests |
|
|
| History (`/history`) | **COMPLETE** | Table view with date filtering, pagination, decision styling, 26 tests |
|
|
| Period History (`/period-history`) | **COMPLETE** | Period log table with edit/delete, cycle lengths, average, prediction accuracy, 27 tests |
|
|
| Plan (`/plan`) | **COMPLETE** | Phase overview, training guidelines, rebounding techniques, 16 tests |
|
|
|
|
### Components
|
|
| Component | Status | Notes |
|
|
|-----------|--------|-------|
|
|
| `DecisionCard` | **COMPLETE** | Displays status, icon, reason |
|
|
| `DataPanel` | **COMPLETE** | Shows BB, HRV, intensity data |
|
|
| `NutritionPanel` | **COMPLETE** | Shows seeds, carbs, keto guidance |
|
|
| `OverrideToggles` | **COMPLETE** | Toggle buttons with callbacks |
|
|
| `DayCell` | **COMPLETE** | Phase-colored day with click handler |
|
|
| `MiniCalendar` | **COMPLETE** | Compact calendar widget with phase colors, navigation, legend (23 tests) |
|
|
| `OnboardingBanner` | **COMPLETE** | Setup prompts for new users with icons and action buttons, 16 tests |
|
|
| `MonthView` | **COMPLETE** | Calendar grid with DayCell integration, navigation controls, phase legend, keyboard navigation |
|
|
| `PeriodDateModal` | **COMPLETE** | Period date input modal with validation, error handling, accessibility (22 tests) |
|
|
| `Skeletons` | **COMPLETE** | Loading skeleton components for all dashboard sections with shimmer animation (29 tests) |
|
|
|
|
### Test Coverage
|
|
| Test File | Status |
|
|
|-----------|--------|
|
|
| `src/lib/cycle.test.ts` | **EXISTS** - 22 tests |
|
|
| `src/lib/decision-engine.test.ts` | **EXISTS** - 24 tests (8 algorithmic rules + 16 override scenarios) |
|
|
| `src/lib/pocketbase.test.ts` | **EXISTS** - 9 tests (auth helpers, cookie loading) |
|
|
| `src/lib/auth-middleware.test.ts` | **EXISTS** - 9 tests (withAuth wrapper, error handling, structured logging) |
|
|
| `src/lib/logger.test.ts` | **EXISTS** - 16 tests (JSON format, log levels, error serialization, child loggers) |
|
|
| `src/lib/metrics.test.ts` | **EXISTS** - 18 tests (metrics collection, counters, gauges, histograms, Prometheus format) |
|
|
| `src/middleware.test.ts` | **EXISTS** - 12 tests (page protection, public routes, static assets) |
|
|
| `src/app/api/user/route.test.ts` | **EXISTS** - 21 tests (GET/PATCH profile, auth, validation, security) |
|
|
| `src/app/api/cycle/period/route.test.ts` | **EXISTS** - 13 tests (POST period, auth, validation, date checks, prediction tracking) |
|
|
| `src/app/api/cycle/current/route.test.ts` | **EXISTS** - 10 tests (GET current cycle, auth, all phases, rollover, custom lengths) |
|
|
| `src/app/api/today/route.test.ts` | **EXISTS** - 22 tests (daily snapshot, auth, decision, overrides, phases, nutrition, biometrics) |
|
|
| `src/app/api/overrides/route.test.ts` | **EXISTS** - 14 tests (POST/DELETE overrides, auth, validation, type checks) |
|
|
| `src/app/login/page.test.tsx` | **EXISTS** - 32 tests (form rendering, auth flow, error handling, validation, accessibility, rate limiting) |
|
|
| `src/app/page.test.tsx` | **EXISTS** - 28 tests (data fetching, component rendering, override toggles, error handling) |
|
|
| `src/lib/nutrition.test.ts` | **EXISTS** - 17 tests (seed cycling, carb ranges, keto guidance by phase) |
|
|
| `src/lib/email.test.ts` | **EXISTS** - 30 tests (email content, subject lines, formatting, token expiration warnings, structured logging) |
|
|
| `src/lib/ics.test.ts` | **EXISTS** - 33 tests (ICS format validation, 90-day event generation, timezone handling, period prediction feedback, CATEGORIES for colors) |
|
|
| `src/lib/encryption.test.ts` | **EXISTS** - 14 tests (encrypt/decrypt round-trip, error handling, key validation) |
|
|
| `src/lib/garmin.test.ts` | **EXISTS** - 33 tests (fetchGarminData, fetchHrvStatus, fetchBodyBattery, fetchIntensityMinutes, token expiry, error handling) |
|
|
| `src/app/api/garmin/tokens/route.test.ts` | **EXISTS** - 15 tests (POST/DELETE tokens, encryption, validation, auth) |
|
|
| `src/app/api/garmin/status/route.test.ts` | **EXISTS** - 11 tests (connection status, expiry, warning levels) |
|
|
| `src/app/api/cron/garmin-sync/route.test.ts` | **EXISTS** - 32 tests (auth, user iteration, token handling, Garmin data fetching, DailyLog creation, token expiration warnings, error handling) |
|
|
| `src/app/api/cron/notifications/route.test.ts` | **EXISTS** - 20 tests (timezone matching, DailyLog handling, email sending) |
|
|
| `src/app/api/calendar/[userId]/[token].ics/route.test.ts` | **EXISTS** - 11 tests (token validation, ICS generation with period prediction feedback, caching, error handling) |
|
|
| `src/app/api/calendar/regenerate-token/route.test.ts` | **EXISTS** - 9 tests (token generation, URL formatting, auth) |
|
|
| `src/app/api/history/route.test.ts` | **EXISTS** - 19 tests (pagination, date filtering, auth, validation) |
|
|
| `src/app/api/period-history/route.test.ts` | **EXISTS** - 18 tests (pagination, cycle length calculation, average, auth) |
|
|
| `src/app/api/period-logs/[id]/route.test.ts` | **EXISTS** - 16 tests (PATCH edit, DELETE with user update, auth, validation) |
|
|
| `src/app/api/health/route.test.ts` | **EXISTS** - 14 tests (healthy/unhealthy states, PocketBase connectivity, error handling) |
|
|
| `src/app/history/page.test.tsx` | **EXISTS** - 26 tests (rendering, data loading, pagination, date filtering, styling) |
|
|
| `src/app/period-history/page.test.tsx` | **EXISTS** - 27 tests (rendering, edit/delete modals, pagination, prediction accuracy) |
|
|
| `src/app/api/metrics/route.test.ts` | **EXISTS** - 15 tests (Prometheus format validation, metric types, route handling) |
|
|
| `src/components/calendar/month-view.test.tsx` | **EXISTS** - 30 tests (calendar grid, phase colors, navigation, legend, keyboard navigation) |
|
|
| `src/app/calendar/page.test.tsx` | **EXISTS** - 23 tests (rendering, navigation, ICS subscription, token regeneration) |
|
|
| `src/app/settings/page.test.tsx` | **EXISTS** - 34 tests (form rendering, validation, submission, accessibility, logout functionality) |
|
|
| `src/app/api/auth/logout/route.test.ts` | **EXISTS** - 5 tests (cookie clearing, success response, error handling) |
|
|
| `src/app/settings/garmin/page.test.tsx` | **EXISTS** - 27 tests (connection status, token management) |
|
|
| `src/components/dashboard/decision-card.test.tsx` | **EXISTS** - 19 tests (rendering, status icons, styling, color-coded backgrounds) |
|
|
| `src/components/dashboard/data-panel.test.tsx` | **EXISTS** - 18 tests (biometrics display, null handling, styling) |
|
|
| `src/components/dashboard/nutrition-panel.test.tsx` | **EXISTS** - 12 tests (seeds, carbs, keto guidance) |
|
|
| `src/components/dashboard/override-toggles.test.tsx` | **EXISTS** - 18 tests (toggle states, callbacks, styling) |
|
|
| `src/components/dashboard/mini-calendar.test.tsx` | **EXISTS** - 23 tests (calendar grid, phase colors, navigation, legend) |
|
|
| `src/components/dashboard/onboarding-banner.test.tsx` | **EXISTS** - 16 tests (setup prompts, icons, action buttons, interactions, dismissal) |
|
|
| `src/components/calendar/day-cell.test.tsx` | **EXISTS** - 27 tests (phase coloring, today highlighting, click handling, accessibility) |
|
|
| `src/app/plan/page.test.tsx` | **EXISTS** - 16 tests (loading states, error handling, phase display, exercise reference, rebounding techniques) |
|
|
| `src/app/layout.test.tsx` | **EXISTS** - 4 tests (skip navigation link rendering, accessibility, Toaster rendering) |
|
|
| `src/components/ui/toaster.test.tsx` | **EXISTS** - 23 tests (toast rendering, types, auto-dismiss, error persistence, accessibility) |
|
|
|
|
### Critical Business Rules (from Spec)
|
|
1. **Override Priority:** flare > stress > sleep > pms (must be enforced in order)
|
|
2. **HRV Unbalanced:** ALWAYS forces REST (highest algorithmic priority, non-overridable)
|
|
3. **Phase Limits:** Strictly enforced per phase configuration
|
|
4. **Token Expiration Warnings:** Must send email at 14 days and 7 days before expiry (IMPLEMENTED - P3.9 COMPLETE)
|
|
5. **ICS Feed:** Generates 90 days of phase events for calendar subscription
|
|
|
|
---
|
|
|
|
## P0: Critical Blockers ✅ ALL COMPLETE
|
|
|
|
These must be completed first - nothing else works without them.
|
|
|
|
### P0.1: PocketBase Auth Helpers ✅ COMPLETE
|
|
- [x] Add authentication utilities to pocketbase.ts
|
|
- **Files:**
|
|
- `src/lib/pocketbase.ts` - Added `createPocketBaseClient()`, `getCurrentUser()`, `isAuthenticated()`, `loadAuthFromCookies()`
|
|
- **Tests:**
|
|
- `src/lib/pocketbase.test.ts` - 9 tests covering auth state management, cookie loading
|
|
- **Why:** Every protected route and page depends on these helpers
|
|
- **Blocking:** P0.2, P0.4, P1.1-P1.7, P2.2-P2.13
|
|
|
|
### P0.2: Auth Middleware for API Routes ✅ COMPLETE
|
|
- [x] Create reusable auth middleware for protected API endpoints
|
|
- **Files:**
|
|
- `src/lib/auth-middleware.ts` - Added `withAuth()` wrapper for route handlers
|
|
- `src/middleware.ts` - Added Next.js middleware for page protection
|
|
- **Tests:**
|
|
- `src/lib/auth-middleware.test.ts` - 6 tests covering unauthorized rejection, user context passing, error handling
|
|
- `src/middleware.test.ts` - 12 tests covering protected routes, public routes, API routes, static assets
|
|
- **Why:** All API routes except `/api/calendar/[userId]/[token].ics` and `/api/cron/*` require auth
|
|
- **Depends On:** P0.1
|
|
- **Blocking:** P0.4, P1.1-P1.5
|
|
|
|
### P0.3: Decision Engine Override Handling ✅ COMPLETE
|
|
- [x] Add override priority logic before algorithmic decision
|
|
- **Files:**
|
|
- `src/lib/decision-engine.ts` - Added `getDecisionWithOverrides(data, overrides)` function
|
|
- **Tests:**
|
|
- `src/lib/decision-engine.test.ts` - 24 tests covering all 8 priority rules + override scenarios
|
|
- **Override Priority (enforced in this order):**
|
|
1. `flare` - Always forces REST
|
|
2. `stress` - Forces REST
|
|
3. `sleep` - Forces REST
|
|
4. `pms` - Forces REST
|
|
- **Why:** Overrides are core to the user experience per spec
|
|
- **Blocking:** P1.4, P1.5
|
|
|
|
### P0.4: GET /api/user Implementation ✅ COMPLETE
|
|
- [x] Return authenticated user profile
|
|
- **Files:**
|
|
- `src/app/api/user/route.ts` - Implemented GET handler with `withAuth()` wrapper
|
|
- **Tests:**
|
|
- `src/app/api/user/route.test.ts` - 4 tests covering auth, response shape, sensitive field exclusion
|
|
- **Response Shape:**
|
|
- `id`, `email`, `garminConnected`, `cycleLength`, `lastPeriodDate`, `notificationTime`, `timezone`, `activeOverrides`
|
|
- Excludes sensitive fields: `garminOauth1Token`, `garminOauth2Token`, `calendarToken`
|
|
- **Why:** Dashboard and all pages need user context
|
|
- **Depends On:** P0.1, P0.2
|
|
- **Blocking:** P1.7, P2.9, P2.10
|
|
|
|
---
|
|
|
|
## P1: Core Functionality ✅ ALL COMPLETE
|
|
|
|
Minimum viable product - app can be used for daily decisions.
|
|
|
|
### P1.1: PATCH /api/user Implementation ✅ COMPLETE
|
|
- [x] Allow profile updates (cycleLength, notificationTime, timezone)
|
|
- **Files:**
|
|
- `src/app/api/user/route.ts` - Implemented PATCH handler with validation
|
|
- **Tests:**
|
|
- `src/app/api/user/route.test.ts` - 17 tests covering field validation, persistence, security
|
|
- **Validation Rules:**
|
|
- `cycleLength`: number, range 21-45 days
|
|
- `notificationTime`: string, HH:MM format (24-hour)
|
|
- `timezone`: non-empty string
|
|
- **Security:** Ignores attempts to update non-updatable fields (email, tokens)
|
|
- **Why:** Users need to configure their cycle and preferences
|
|
- **Depends On:** P0.1, P0.2
|
|
|
|
### P1.2: POST /api/cycle/period Implementation ✅ COMPLETE
|
|
- [x] Log period start date, update user record, create PeriodLog
|
|
- **Files:**
|
|
- `src/app/api/cycle/period/route.ts` - Implemented POST handler with validation and prediction tracking
|
|
- **Tests:**
|
|
- `src/app/api/cycle/period/route.test.ts` - 13 tests covering auth, date validation, user update, PeriodLog creation, prediction tracking
|
|
- **Why:** Cycle tracking is the foundation of all recommendations
|
|
- **Depends On:** P0.1, P0.2
|
|
|
|
### P1.3: GET /api/cycle/current Implementation ✅ COMPLETE
|
|
- [x] Return current cycle day, phase, and phase config
|
|
- **Files:**
|
|
- `src/app/api/cycle/current/route.ts` - Implemented GET using cycle.ts utilities with `withAuth()` wrapper
|
|
- **Tests:**
|
|
- `src/app/api/cycle/current/route.test.ts` - 10 tests covering auth, validation, all phases, cycle rollover, custom cycle lengths
|
|
- **Response Shape:**
|
|
- `cycleDay`, `phase`, `phaseConfig`, `daysUntilNextPhase`, `cycleLength`
|
|
- **Why:** Dashboard needs this for display
|
|
- **Depends On:** P0.1, P0.2, P1.2
|
|
|
|
### P1.4: GET /api/today Implementation ✅ COMPLETE
|
|
- [x] Return complete daily snapshot with decision, biometrics, nutrition
|
|
- **Files:**
|
|
- `src/app/api/today/route.ts` - Implemented GET with `withAuth()` wrapper, aggregates cycle, biometrics, and nutrition
|
|
- **Tests:**
|
|
- `src/app/api/today/route.test.ts` - 22 tests covering auth, validation, decision calculation, overrides, phases, nutrition
|
|
- **Response Shape:**
|
|
- `decision` (status, reason, icon), `cycleDay`, `phase`, `phaseConfig`, `daysUntilNextPhase`, `cycleLength`
|
|
- `biometrics` (hrvStatus, bodyBatteryCurrent, bodyBatteryYesterdayLow, weekIntensityMinutes, phaseLimit)
|
|
- `nutrition` (seeds, carbRange, ketoGuidance)
|
|
- **Fallback Behavior:** When no DailyLog exists (Garmin not synced), returns defaults: hrvStatus="Unknown", BB=100, weekIntensity=0
|
|
- **Why:** This is THE core API for the dashboard
|
|
- **Depends On:** P0.1, P0.2, P0.3, P1.3
|
|
|
|
### P1.5: POST/DELETE /api/overrides Implementation ✅ COMPLETE
|
|
- [x] Toggle override flags on user record
|
|
- **Files:**
|
|
- `src/app/api/overrides/route.ts` - Implemented POST (add) and DELETE (remove) handlers with validation
|
|
- **Tests:**
|
|
- `src/app/api/overrides/route.test.ts` - 14 tests covering auth, override types, persistence, validation, edge cases
|
|
- **Override Types:** flare, stress, sleep, pms
|
|
- **POST Response:** Returns updated user with new override added to activeOverrides array
|
|
- **DELETE Response:** Returns updated user with override removed from activeOverrides array
|
|
- **Validation:** Rejects invalid override types, duplicates on POST, missing overrides on DELETE
|
|
- **Why:** Emergency overrides are critical for flare days
|
|
- **Depends On:** P0.1, P0.2, P0.3
|
|
|
|
### P1.6: Login Page Implementation ✅ COMPLETE
|
|
- [x] Functional login form with PocketBase auth
|
|
- **Files:**
|
|
- `src/app/login/page.tsx` - Client component with email/password form, error handling, loading states, redirect
|
|
- **Tests:**
|
|
- `src/app/login/page.test.tsx` - 14 tests covering rendering, form submission, auth flow, error handling, validation
|
|
- **Infrastructure Added:**
|
|
- `src/test-setup.ts` - Global test setup with @testing-library/jest-dom and cleanup
|
|
- Updated `vitest.config.ts` to include setupFiles
|
|
- **Why:** Users need to authenticate to use the app
|
|
- **Depends On:** P0.1
|
|
|
|
### P1.7: Dashboard Page Implementation ✅ COMPLETE
|
|
- [x] Wire up dashboard with real data from /api/today
|
|
- [x] Integrate DecisionCard, DataPanel, NutritionPanel, OverrideToggles
|
|
- [x] Implement override toggle functionality with optimistic updates
|
|
- [x] Add error handling and loading states
|
|
- **Files:**
|
|
- `src/app/page.tsx` - Client component fetching /api/today, rendering all dashboard components
|
|
- **Tests:**
|
|
- `src/app/page.test.tsx` - 23 tests covering data fetching, component rendering, override toggles, error handling
|
|
- **Features Implemented:**
|
|
- Real-time decision display with cycle phase information
|
|
- Biometrics panel (HRV, Body Battery, Intensity Minutes)
|
|
- Nutrition guidance panel (seeds, carbs, keto)
|
|
- Override toggles with optimistic UI updates
|
|
- Error boundaries and loading states
|
|
- **Why:** This is the main user interface
|
|
- **Depends On:** P0.4, P1.3, P1.4, P1.5
|
|
|
|
---
|
|
|
|
## P2: Important Features
|
|
|
|
Full feature set for production use.
|
|
|
|
### P2.1: Garmin Data Fetching Functions ✅ COMPLETE
|
|
- [x] Add specific fetchers for HRV, Body Battery, Intensity Minutes
|
|
- **Files:**
|
|
- `src/lib/garmin.ts` - Added `fetchHrvStatus()`, `fetchBodyBattery()`, `fetchIntensityMinutes()`
|
|
- **Tests:**
|
|
- `src/lib/garmin.test.ts` - 33 tests covering API calls, response parsing, error handling (increased from 14 tests)
|
|
- **Functions Implemented:**
|
|
- `fetchHrvStatus()` - Fetches HRV status (balanced/unbalanced) from Garmin
|
|
- `fetchBodyBattery()` - Fetches current and yesterday's low body battery values
|
|
- `fetchIntensityMinutes()` - Fetches weekly moderate + vigorous intensity minutes
|
|
- **Why:** Real biometric data is required for accurate decisions
|
|
|
|
### P2.2: POST/DELETE /api/garmin/tokens Implementation ✅ COMPLETE
|
|
- [x] Store encrypted Garmin OAuth tokens
|
|
- **Files:**
|
|
- `src/app/api/garmin/tokens/route.ts` - POST/DELETE handlers with encryption, validation
|
|
- **Tests:**
|
|
- `src/app/api/garmin/tokens/route.test.ts` - 15 tests covering encryption, validation, storage, auth, deletion
|
|
- **Features Implemented:**
|
|
- POST: Accepts oauth1, oauth2, expires_at; encrypts tokens; stores in user record
|
|
- DELETE: Clears tokens and sets garminConnected to false
|
|
- Validation for required fields and types
|
|
- Returns daysUntilExpiry in POST response
|
|
- **Why:** Users need to connect their Garmin accounts
|
|
- **Depends On:** P0.1, P0.2
|
|
|
|
### P2.3: GET /api/garmin/status Implementation ✅ COMPLETE
|
|
- [x] Return Garmin connection status and days until expiry
|
|
- **Files:**
|
|
- `src/app/api/garmin/status/route.ts` - GET handler with expiry calculation
|
|
- **Tests:**
|
|
- `src/app/api/garmin/status/route.test.ts` - 11 tests covering connected/disconnected states, expiry calc, warning levels
|
|
- **Response Shape:**
|
|
- `connected` - Boolean indicating if tokens exist
|
|
- `daysUntilExpiry` - Days until token expires (null if not connected)
|
|
- `expired` - Boolean indicating if tokens have expired
|
|
- `warningLevel` - "critical" (<=7 days), "warning" (8-14 days), or null
|
|
- **Why:** Users need visibility into their Garmin connection
|
|
- **Depends On:** P0.1, P0.2, P2.1
|
|
|
|
### P2.4: POST /api/cron/garmin-sync Implementation ✅ COMPLETE
|
|
- [x] Daily sync of all Garmin data for all users
|
|
- **Files:**
|
|
- `src/app/api/cron/garmin-sync/route.ts` - Iterates users, fetches data, stores DailyLog
|
|
- **Tests:**
|
|
- `src/app/api/cron/garmin-sync/route.test.ts` - 32 tests covering auth, user iteration, token handling, Garmin data fetching, DailyLog creation, token expiration warnings, error handling
|
|
- **Features Implemented:**
|
|
- Fetches all users with garminConnected=true
|
|
- Skips users with expired tokens
|
|
- Decrypts OAuth2 tokens and fetches HRV, Body Battery, Intensity Minutes
|
|
- Calculates cycle day, phase, phase limit, remaining minutes
|
|
- Computes training decision using decision engine
|
|
- Creates DailyLog entries for each user
|
|
- Sends token expiration warning emails at 14 and 7 days before expiry
|
|
- Returns sync summary (usersProcessed, errors, skippedExpired, timestamp)
|
|
- **Why:** Automated data sync is required for morning notifications
|
|
- **Depends On:** P2.1, P2.2
|
|
|
|
### P2.5: POST /api/cron/notifications Implementation ✅ COMPLETE
|
|
- [x] Send daily email notifications at user's preferred time
|
|
- **Files:**
|
|
- `src/app/api/cron/notifications/route.ts` - Timezone-aware user matching, DailyLog fallback, email sending
|
|
- **Tests:**
|
|
- `src/app/api/cron/notifications/route.test.ts` - 20 tests covering timezone matching, DailyLog handling, email sending
|
|
- **Features Implemented:**
|
|
- Timezone-aware notification matching (finds users whose notificationTime matches current hour in their timezone)
|
|
- DailyLog-based notifications with fallback to real-time calculation when DailyLog missing
|
|
- Duplicate prevention (only sends once per user per hour)
|
|
- Nutrition guidance integration (seeds, carbs, keto)
|
|
- CRON_SECRET authentication
|
|
- Returns summary with emailsSent count and timestamp
|
|
- **Why:** Email notifications are a key feature per spec
|
|
- **Depends On:** P2.4
|
|
|
|
### P2.6: GET /api/calendar/[userId]/[token].ics Implementation ✅ COMPLETE
|
|
- [x] Return ICS feed for calendar subscription
|
|
- **Files:**
|
|
- `src/app/api/calendar/[userId]/[token].ics/route.ts` - Validates token, generates ICS with 90 days of phase events and period prediction feedback
|
|
- **Tests:**
|
|
- `src/app/api/calendar/[userId]/[token].ics/route.test.ts` - 11 tests covering token validation, ICS generation with period predictions, caching headers, error handling
|
|
- **Features Implemented:**
|
|
- Token-based authentication (no session required)
|
|
- Validates calendar token against user record
|
|
- Fetches period logs and passes them to ICS generator for prediction feedback
|
|
- Generates 90 days of phase events using `generateIcsFeed()`
|
|
- Returns proper Content-Type header (`text/calendar; charset=utf-8`)
|
|
- Caching headers for calendar client optimization
|
|
- 404 for non-existent users, 401 for invalid tokens
|
|
- **Why:** Calendar integration for external apps
|
|
|
|
### P2.7: POST /api/calendar/regenerate-token Implementation ✅ COMPLETE
|
|
- [x] Generate new calendar token
|
|
- **Files:**
|
|
- `src/app/api/calendar/regenerate-token/route.ts` - Creates random 32-char token, updates user
|
|
- **Tests:**
|
|
- `src/app/api/calendar/regenerate-token/route.test.ts` - 9 tests covering token generation, URL formatting, auth
|
|
- **Features Implemented:**
|
|
- Requires authentication via `withAuth()` middleware
|
|
- Generates cryptographically secure 32-character hex token
|
|
- Updates user's `calendarToken` field in database
|
|
- Returns new token and formatted calendar URL
|
|
- Old tokens immediately invalidated
|
|
- **Why:** Security feature for calendar URLs
|
|
- **Depends On:** P0.1, P0.2
|
|
|
|
### P2.8: GET /api/history Implementation ✅ COMPLETE
|
|
- [x] Return paginated historical daily logs
|
|
- **Files:**
|
|
- `src/app/api/history/route.ts` - Query DailyLog with pagination, date filtering, validation
|
|
- **Tests:**
|
|
- `src/app/api/history/route.test.ts` - 19 tests covering pagination, date filtering, auth, validation
|
|
- **Features Implemented:**
|
|
- Pagination with page/limit parameters (default: page=1, limit=20)
|
|
- Date filtering with startDate/endDate query params (YYYY-MM-DD format)
|
|
- Validation for all parameters with descriptive error messages
|
|
- Sort by date descending (most recent first)
|
|
- Returns items, total, page, limit, totalPages, hasMore
|
|
- **Why:** Users want to see their training history
|
|
- **Depends On:** P0.1, P0.2
|
|
|
|
### P2.9: Settings Page Implementation ✅ COMPLETE
|
|
- [x] User profile management UI
|
|
- **Files:**
|
|
- `src/app/settings/page.tsx` - Form for cycleLength, notificationTime, timezone with validation, loading states, error handling
|
|
- **Tests:**
|
|
- `src/app/settings/page.test.tsx` - 24 tests covering rendering, data loading, form submission, validation, error handling
|
|
- **Why:** Users need to configure their preferences
|
|
- **Depends On:** P0.4, P1.1
|
|
|
|
### P2.10: Settings/Garmin Page Implementation ✅ COMPLETE
|
|
- [x] Garmin connection management UI
|
|
- **Files:**
|
|
- `src/app/settings/garmin/page.tsx` - Token input form, connection status, expiry warnings, disconnect button
|
|
- **Tests:**
|
|
- `src/app/settings/garmin/page.test.tsx` - 27 tests covering rendering, connection states, warning levels, token submission, disconnect flow
|
|
- `src/app/settings/page.test.tsx` - 3 additional tests for Garmin link (28 total)
|
|
- **Features Implemented:**
|
|
- Connection status display with green/red/gray indicators
|
|
- Token expiry warnings (yellow for 14 days, red for 7 days)
|
|
- Token input form with JSON validation
|
|
- Instructions for running bootstrap script
|
|
- Disconnect functionality
|
|
- Loading and error states
|
|
- **Why:** Users need to manage their Garmin connection
|
|
- **Depends On:** P0.4, P2.2, P2.3
|
|
|
|
### P2.11: Calendar Page Implementation ✅ COMPLETE
|
|
- [x] In-app calendar with phase visualization
|
|
- **Files:**
|
|
- `src/app/calendar/page.tsx` - Month view with navigation, ICS subscription section with URL display, copy button, token regeneration
|
|
- `src/components/calendar/month-view.tsx` - Complete calendar grid with DayCell integration, navigation controls, phase legend
|
|
- **Tests:**
|
|
- `src/components/calendar/month-view.test.tsx` - 21 tests covering calendar grid, phase colors, navigation, legend
|
|
- `src/app/calendar/page.test.tsx` - 23 tests covering rendering, navigation, ICS subscription, token regeneration
|
|
- **Why:** Planning ahead is a key user need
|
|
- **Depends On:** P2.6
|
|
|
|
### P2.12: History Page Implementation ✅ COMPLETE
|
|
- [x] View past training decisions and data
|
|
- **Files:**
|
|
- `src/app/history/page.tsx` - Data fetching, table display, pagination, date filtering
|
|
- **Tests:**
|
|
- `src/app/history/page.test.tsx` - 26 tests covering rendering, data loading, pagination, filtering, error handling
|
|
- **Why:** Users want to review their training history
|
|
- **Depends On:** P2.8
|
|
|
|
### P2.13: Plan Page Implementation ✅ COMPLETE
|
|
- [x] Phase-specific training plan view
|
|
- **Files:**
|
|
- `src/app/plan/page.tsx` - Phase overview, training guidelines, exercise reference, rebounding techniques
|
|
- **Tests:**
|
|
- `src/app/plan/page.test.tsx` - 16 tests covering loading states, error handling, phase display, exercise reference, rebounding techniques
|
|
- **Features Implemented:**
|
|
- Current phase status display (day, phase name, training type, weekly limit)
|
|
- Phase overview cards for all 5 phases with weekly intensity minute limits
|
|
- Strength training exercises reference with descriptions
|
|
- Rebounding techniques organized by phase (follicular and luteal)
|
|
- Weekly guidelines for each phase with training goals
|
|
- **Why:** Users want detailed training guidance
|
|
- **Depends On:** P0.4, P1.3
|
|
|
|
### P2.14: Mini Calendar Component ✅ COMPLETE
|
|
- [x] Dashboard overview calendar
|
|
- **Current State:** COMPLETE - Compact calendar grid with phase colors, navigation buttons, today highlighting, phase legend
|
|
- **Files:**
|
|
- `src/components/dashboard/mini-calendar.tsx` - Complete calendar grid with DayCell integration
|
|
- **Tests:**
|
|
- `src/components/dashboard/mini-calendar.test.tsx` - 23 tests (calendar grid, phase colors, navigation, legend)
|
|
- **Features Implemented:**
|
|
- Calendar grid using DayCell component
|
|
- Current week/month view
|
|
- Phase color coding
|
|
- Today highlight
|
|
- Navigation buttons (prev/next month)
|
|
- Phase legend
|
|
- **Why:** Quick visual reference on dashboard
|
|
|
|
### P2.15: Health Check Endpoint ✅ COMPLETE
|
|
- [x] GET /api/health for deployment monitoring
|
|
- **Current State:** Fully implemented with PocketBase connectivity checks
|
|
- **Files:**
|
|
- `src/app/api/health/route.ts` - Returns health status with PocketBase connectivity check
|
|
- **Tests:**
|
|
- `src/app/api/health/route.test.ts` - 14 tests for healthy (200) and unhealthy (503) states
|
|
- **Response Shape:**
|
|
- `status` - "ok" or "unhealthy"
|
|
- `timestamp` - ISO 8601 timestamp
|
|
- `version` - Application version
|
|
- **Checks Performed:**
|
|
- PocketBase connectivity
|
|
- Basic app startup complete
|
|
- **Why:** Required for Nomad health checks, load balancer probes, and uptime monitoring (per specs/observability.md)
|
|
|
|
### P2.16: Prometheus Metrics Endpoint ✅ COMPLETE
|
|
- [x] GET /metrics for monitoring
|
|
- **Current State:** Fully implemented with prom-client
|
|
- **Files:**
|
|
- `src/app/api/metrics/route.ts` - Returns Prometheus-format metrics (15 tests)
|
|
- `src/lib/metrics.ts` - Metrics collection with prom-client (18 tests)
|
|
- **Tests:**
|
|
- `src/lib/metrics.test.ts` - 18 tests covering metrics collection, counters, gauges, histograms, Prometheus format
|
|
- `src/app/api/metrics/route.test.ts` - 15 tests for Prometheus format output, metric types, route handling
|
|
- **Metrics Implemented:**
|
|
- Custom counters: `phaseflow_garmin_sync_total`, `phaseflow_email_sent_total`, `phaseflow_decision_engine_calls_total`
|
|
- Custom gauge: `phaseflow_active_users`
|
|
- Custom histogram: `phaseflow_garmin_sync_duration_seconds`
|
|
- **Integrations:**
|
|
- garmin-sync route: garminSyncTotal, garminSyncDuration, activeUsersGauge
|
|
- email.ts: emailSentTotal (daily and warning types)
|
|
- decision-engine.ts: decisionEngineCallsTotal
|
|
- **Why:** Required for Prometheus scraping and production monitoring (per specs/observability.md)
|
|
### P2.17: Structured Logging with Pino ✅ COMPLETE
|
|
- [x] Create pino-based logger with JSON output
|
|
- **Files:**
|
|
- `src/lib/logger.ts` - Pino logger configuration with LOG_LEVEL env var support
|
|
- **Tests:**
|
|
- `src/lib/logger.test.ts` - 16 tests covering JSON format, log levels, error stack traces, child loggers
|
|
- **Features Implemented:**
|
|
- JSON output to stdout for log aggregators (Loki, ELK)
|
|
- Log levels: error, warn, info, debug
|
|
- LOG_LEVEL environment variable configuration (defaults to "info")
|
|
- Error objects serialized with type, message, and stack trace
|
|
- Child logger support for bound context
|
|
- ISO 8601 timestamps
|
|
- **Why:** Required for log aggregators and production debugging (per specs/observability.md)
|
|
- **Next Step:** Integrate logger into API routes (can be done incrementally)
|
|
|
|
### P2.18: OIDC Authentication ✅ COMPLETE
|
|
- [x] Replace email/password login with OIDC (Pocket-ID)
|
|
- **Files:**
|
|
- `src/app/login/page.tsx` - OIDC button with email/password fallback
|
|
- **Tests:**
|
|
- `src/app/login/page.test.tsx` - 24 tests (10 new OIDC tests)
|
|
- **Features Implemented:**
|
|
- Auto-detection of OIDC provider via `listAuthMethods()` API
|
|
- "Sign In with Pocket-ID" button when OIDC provider is configured
|
|
- Email/password form fallback when OIDC is not available
|
|
- PocketBase `authWithOAuth2()` popup-based OAuth2 flow
|
|
- Loading states during authentication
|
|
- Error handling with user-friendly messages
|
|
- **Flow:**
|
|
1. Page checks for available auth methods on mount
|
|
2. If OIDC provider configured, shows "Sign In with Pocket-ID" button
|
|
3. User clicks button, PocketBase handles OAuth2 popup flow
|
|
4. On success, user redirected to dashboard
|
|
5. Falls back to email/password when OIDC not available
|
|
- **Environment Variables (configured in PocketBase Admin):**
|
|
- Client ID, Client Secret, Issuer URL configured in PocketBase
|
|
- **Why:** Required per specs/authentication.md for secure identity management
|
|
|
|
---
|
|
|
|
## P3: Polish and Quality
|
|
|
|
Testing, error handling, and refinements.
|
|
|
|
### P3.1: Decision Engine Tests ✅ COMPLETE
|
|
- [x] Comprehensive unit tests for all decision paths
|
|
- **Files:**
|
|
- `src/lib/decision-engine.test.ts` - All 8 priority rules, override combinations (24 tests)
|
|
- **Test Cases Covered:**
|
|
- HRV Unbalanced always forces REST (highest algorithmic priority)
|
|
- Override priority: flare > stress > sleep > pms
|
|
- Phase limits strictly enforced
|
|
- All override bypass and fallthrough scenarios
|
|
- **Why:** Critical logic is now fully tested
|
|
|
|
### P3.2: Nutrition Tests ✅ COMPLETE
|
|
- [x] Unit tests for nutrition guidance
|
|
- **Files:**
|
|
- `src/lib/nutrition.test.ts` - 17 tests covering seed cycling, carb ranges, keto guidance by phase
|
|
- **Test Cases Covered:**
|
|
- Seed cycling recommendations by phase (flax/pumpkin vs sunflower/sesame)
|
|
- Carb range calculations per phase
|
|
- Keto guidance by cycle day
|
|
- Edge cases and phase transitions
|
|
- **Why:** Nutrition advice accuracy is now fully tested
|
|
|
|
### P3.3: Email Tests ✅ COMPLETE
|
|
- [x] Unit tests for email composition
|
|
- **Files:**
|
|
- `src/lib/email.test.ts` - 14 tests covering email content, subject lines, formatting
|
|
- **Test Cases Covered:**
|
|
- Daily email composition with decision data
|
|
- Period confirmation email content
|
|
- Subject line formatting
|
|
- HTML email structure
|
|
- **Why:** Email formatting correctness is now fully tested
|
|
|
|
### P3.4: ICS Tests ✅ COMPLETE
|
|
- [x] Unit tests for calendar generation
|
|
- **Files:**
|
|
- `src/lib/ics.test.ts` - 28 tests covering ICS format validation, 90-day event generation, timezone handling, period prediction feedback
|
|
- **Test Cases Covered:**
|
|
- ICS feed generation with 90 days of phase events
|
|
- RFC 5545 format compliance
|
|
- Timezone handling (UTC conversion)
|
|
- Event boundaries and phase transitions
|
|
- Period prediction accuracy feedback ("Predicted" labels)
|
|
- **Why:** Calendar integration compatibility is now fully tested
|
|
|
|
### P3.5: Encryption Tests ✅ COMPLETE
|
|
- [x] Unit tests for encrypt/decrypt round-trip
|
|
- **Files:**
|
|
- `src/lib/encryption.test.ts` - 14 tests covering AES-256-GCM round-trip, error handling, key validation
|
|
- **Test Cases Covered:**
|
|
- Encrypt/decrypt round-trip verification
|
|
- Key validation and error handling
|
|
- IV generation uniqueness
|
|
- Malformed data handling
|
|
- **Why:** Token security is now fully tested
|
|
|
|
### P3.6: Garmin Tests ✅ COMPLETE
|
|
- [x] Unit tests for Garmin API interactions
|
|
- **Files:**
|
|
- `src/lib/garmin.test.ts` - 33 tests covering API calls, error handling, token expiry (expanded in P2.1)
|
|
- **Test Cases Covered:**
|
|
- fetchGarminData, fetchHrvStatus, fetchBodyBattery, fetchIntensityMinutes HTTP calls and response parsing
|
|
- isTokenExpired logic with various expiry dates
|
|
- daysUntilExpiry calculations
|
|
- Error handling for invalid tokens and network failures
|
|
- Response parsing for biometric data structures
|
|
- **Why:** External API integration robustness is now fully tested
|
|
|
|
### P3.7: Error Handling Improvements ✅ COMPLETE
|
|
- [x] Add consistent error responses across all API routes
|
|
- [x] Replace console.error with structured pino logger
|
|
- [x] Add logging for key events per observability spec
|
|
- **Files:**
|
|
- `src/lib/auth-middleware.ts` - Replaced console.error with structured logger, added auth failure logging
|
|
- `src/app/api/cycle/period/route.ts` - Added "Period logged" event logging, structured error logging
|
|
- `src/app/api/calendar/[userId]/[token].ics/route.ts` - Replaced console.error with structured logger
|
|
- `src/app/api/overrides/route.ts` - Added "Override toggled" event logging
|
|
- `src/app/api/today/route.ts` - Added "Decision calculated" event logging
|
|
- `src/app/api/cron/garmin-sync/route.ts` - Added "Garmin sync start", "Garmin sync complete", "Garmin sync failure" logging
|
|
- `src/app/api/auth/logout/route.ts` - Added "User logged out" logging
|
|
- **Tests:**
|
|
- `src/lib/auth-middleware.test.ts` - Added 3 tests for structured logging (9 total)
|
|
- **Events Logged (per observability spec):**
|
|
- Auth failure (warn): reason
|
|
- Period logged (info): userId, date
|
|
- Override toggled (info): userId, override, enabled
|
|
- Decision calculated (info): userId, decision, reason
|
|
- Garmin sync start (info): userId
|
|
- Garmin sync complete (info): userId, duration_ms, metrics (bodyBattery, hrvStatus)
|
|
- Garmin sync failure (error): userId, err object
|
|
- User logged out (info)
|
|
- Error events (error): err object with stack trace
|
|
- **Why:** Better debugging and user experience with structured JSON logs
|
|
|
|
### P3.8: Loading States ✅ COMPLETE
|
|
- [x] Add loading indicators to all pages
|
|
- **Files:**
|
|
- `src/components/dashboard/skeletons.tsx` - Skeleton components (DecisionCardSkeleton, DataPanelSkeleton, NutritionPanelSkeleton, MiniCalendarSkeleton, OverrideTogglesSkeleton, CycleInfoSkeleton, DashboardSkeleton)
|
|
- `src/components/dashboard/skeletons.test.tsx` - 29 tests
|
|
- `src/app/loading.tsx` - Dashboard route loading state
|
|
- `src/app/calendar/loading.tsx` - Calendar route loading state
|
|
- `src/app/history/loading.tsx` - History route loading state
|
|
- `src/app/plan/loading.tsx` - Plan route loading state
|
|
- `src/app/settings/loading.tsx` - Settings route loading state
|
|
- **Features:** Skeleton placeholders with shimmer animations matching spec requirements, updated dashboard page to use skeleton components
|
|
- **Why:** Better perceived performance
|
|
### P3.9: Token Expiration Warnings ✅ COMPLETE
|
|
- [x] Email warnings at 14 and 7 days before Garmin token expiry
|
|
- **Files:**
|
|
- `src/lib/email.ts` - Added `sendTokenExpirationWarning()` function
|
|
- `src/app/api/cron/garmin-sync/route.ts` - Added token expiry checking and warning logic
|
|
- **Tests:**
|
|
- `src/lib/email.test.ts` - 10 new tests for warning email function (24 total)
|
|
- `src/app/api/cron/garmin-sync/route.test.ts` - 10 new tests for warning integration (32 total)
|
|
- **Features Implemented:**
|
|
- Sends warning email at exactly 14 days before token expiry
|
|
- Sends warning email at exactly 7 days before token expiry
|
|
- Warning logic integrated into garmin-sync cron job
|
|
- Email includes days until expiry and instructions for refreshing tokens
|
|
- **Why:** Users need time to refresh tokens (per spec requirement in specs/email.md)
|
|
|
|
### P3.10: E2E Test Suite
|
|
- [ ] Comprehensive end-to-end tests
|
|
- **Files:**
|
|
- `tests/e2e/*.spec.ts` - Full user flows
|
|
- **Test Scenarios:**
|
|
- Login flow
|
|
- Period logging and phase calculation
|
|
- Override toggle functionality
|
|
- Settings update flow
|
|
- Garmin connection flow
|
|
- Calendar subscription
|
|
- **Why:** Confidence in production deployment
|
|
|
|
### P3.11: Missing Component Tests ✅ COMPLETE
|
|
- [x] Add unit tests for untested components
|
|
- **Components Tested (5 total):**
|
|
- `src/components/dashboard/decision-card.tsx` - 19 tests for rendering decision status, icon, reason, styling, color-coded backgrounds
|
|
- `src/components/dashboard/data-panel.tsx` - 18 tests for biometrics display (BB, HRV, intensity), null handling, styling
|
|
- `src/components/dashboard/nutrition-panel.tsx` - 12 tests for seeds, carbs, keto guidance display
|
|
- `src/components/dashboard/override-toggles.tsx` - 18 tests for toggle states, callbacks, styling
|
|
- `src/components/calendar/day-cell.tsx` - 23 tests for phase coloring, today highlighting, click handling
|
|
- **Test Files Created:**
|
|
- `src/components/dashboard/decision-card.test.tsx` - 19 tests
|
|
- `src/components/dashboard/data-panel.test.tsx` - 18 tests
|
|
- `src/components/dashboard/nutrition-panel.test.tsx` - 12 tests
|
|
- `src/components/dashboard/override-toggles.test.tsx` - 18 tests
|
|
- `src/components/calendar/day-cell.test.tsx` - 23 tests
|
|
- **Total Tests Added:** 90 tests across 5 files
|
|
- **Why:** Component isolation ensures UI correctness and prevents regressions
|
|
|
|
---
|
|
|
|
## P4: UX Polish and Accessibility
|
|
|
|
Enhancements from spec requirements that improve user experience.
|
|
|
|
### P4.1: Dashboard Onboarding Banners ✅ COMPLETE
|
|
- [x] Show setup prompts for missing configuration
|
|
- **Spec Reference:** specs/dashboard.md mentions onboarding banners
|
|
- **Implementation Details:**
|
|
- `OnboardingBanner` component created at `src/components/dashboard/onboarding-banner.tsx`
|
|
- Tests at `src/components/dashboard/onboarding-banner.test.tsx` - 16 tests
|
|
- Dashboard integration at `src/app/page.tsx` - 5 new tests added to page.test.tsx (28 total)
|
|
- Features: Garmin connection banner, period date banner with callback, notification time banner
|
|
- Banners render at top of dashboard when setup incomplete
|
|
- **Files:**
|
|
- `src/app/page.tsx` - Added conditional banner rendering at top of dashboard
|
|
- `src/components/dashboard/onboarding-banner.tsx` - New component with icons, action buttons, styling
|
|
- `src/components/dashboard/onboarding-banner.test.tsx` - 16 tests covering rendering, interactions, dismissal
|
|
- **Why:** Helps new users complete setup for full functionality
|
|
|
|
### P4.2: Accessibility Improvements ✅ COMPLETE
|
|
- [x] Skip navigation link
|
|
- [x] Semantic HTML landmarks (main elements)
|
|
- [x] Screen reader labels for calendar buttons
|
|
- [x] Keyboard navigation for calendar
|
|
- **Spec Reference:** specs/dashboard.md accessibility requirements
|
|
- **Implementation Details:**
|
|
- Skip navigation link added to layout with sr-only styling
|
|
- Semantic HTML landmarks (main element) added to login and settings pages
|
|
- Aria-labels added to DayCell calendar buttons with date and phase information
|
|
- Keyboard navigation: ArrowLeft/Right for prev/next day, ArrowUp/Down for prev/next week, Home/End for first/last day, boundary navigation triggers month change
|
|
- **Files:**
|
|
- `src/app/layout.tsx` - Added skip navigation link with sr-only styling
|
|
- `src/app/layout.test.tsx` - 3 tests for skip link rendering and accessibility
|
|
- `src/app/login/page.tsx` - Wrapped content in main element
|
|
- `src/app/login/page.test.tsx` - Added 2 accessibility tests (26 total)
|
|
- `src/app/settings/page.tsx` - Wrapped content in main element
|
|
- `src/app/settings/page.test.tsx` - Added 2 accessibility tests (29 total)
|
|
- `src/app/page.tsx` - Added id="main-content" to existing main element
|
|
- `src/components/calendar/day-cell.tsx` - Added aria-label with date/phase info, dataDay prop
|
|
- `src/components/calendar/day-cell.test.tsx` - Added 4 accessibility tests (27 total)
|
|
- `src/components/calendar/month-view.tsx` - Added role="grid", keyboard navigation handler
|
|
- `src/components/calendar/month-view.test.tsx` - Added 9 keyboard navigation tests (30 total)
|
|
- **Why:** Required for accessibility compliance
|
|
|
|
### P4.3: Dark Mode Configuration ✅ COMPLETE
|
|
- [x] Complete dark mode support
|
|
- **Current State:** COMPLETE - Dark mode auto-detects system preference via `prefers-color-scheme`
|
|
- **Implementation Details:**
|
|
- Changed `@custom-variant dark` from class-based to `@media (prefers-color-scheme: dark)` in globals.css
|
|
- Changed `.dark` CSS selector to `@media (prefers-color-scheme: dark) { :root { ... } }` for CSS variables
|
|
- Dark mode now auto-detects system preference (no manual toggle needed per spec)
|
|
- No component changes needed - existing CSS variables handle the theming
|
|
- **Files:**
|
|
- `src/app/globals.css` - Updated dark mode detection to use media query instead of class
|
|
- **Why:** User preference for dark mode
|
|
### P4.4: Loading Performance ✅ COMPLETE
|
|
- [x] Loading states within 100ms target
|
|
- **Spec Reference:** specs/dashboard.md performance requirements
|
|
- **Implementation Details:**
|
|
- Skeleton loading states exist for all routes (P3.8 COMPLETE)
|
|
- Optimistic UI updates implemented for override toggles
|
|
- Suspense boundaries provided by Next.js App Router via loading.tsx files
|
|
- Next.js loading.tsx files render immediately during navigation (< 100ms)
|
|
- **Files:**
|
|
- `src/app/loading.tsx` - Dashboard route loading (uses DashboardSkeleton)
|
|
- `src/app/calendar/loading.tsx` - Calendar route loading
|
|
- `src/app/history/loading.tsx` - History route loading
|
|
- `src/app/plan/loading.tsx` - Plan route loading
|
|
- `src/app/settings/loading.tsx` - Settings route loading
|
|
- `src/components/dashboard/skeletons.tsx` - Skeleton components (29 tests)
|
|
- **Why:** Perceived performance improvement
|
|
- **Verification:** Build succeeds, all 889 tests pass, Next.js handles 100ms target via automatic loading.tsx rendering
|
|
|
|
### P4.5: Period Prediction Accuracy Feedback ✅ COMPLETE
|
|
- [x] Mark predicted vs confirmed period dates
|
|
- **Spec Reference:** specs/calendar.md mentions predictions marked with "Predicted" suffix
|
|
- **Files Modified:**
|
|
- `src/types/index.ts` - Added `predictedDate` field to PeriodLog type
|
|
- `src/lib/ics.ts` - Modified `generateIcsFeed()` to accept period logs and mark events with "(Predicted)" when actual differs from predicted
|
|
- `src/app/api/cycle/period/route.ts` - POST handler calculates predicted date (lastPeriodDate + cycleLength), stores in PeriodLog, returns daysEarly/daysLate
|
|
- `src/app/api/calendar/[userId]/[token].ics/route.ts` - Fetches period logs and passes them to ICS generator
|
|
- **Tests Added:**
|
|
- `src/app/api/cycle/period/route.test.ts` - 5 new tests (13 total): predictedDate storage, daysEarly/daysLate calculations
|
|
- `src/lib/ics.test.ts` - 5 new tests (28 total): "(Predicted)" label on events when actual differs from predicted
|
|
- `src/app/api/calendar/[userId]/[token].ics/route.test.ts` - 1 new test (11 total): period logs fetching and passing to ICS generator
|
|
- **Features Implemented:**
|
|
- PeriodLog stores predictedDate calculated from previous period (lastPeriodDate + cycleLength)
|
|
- POST /api/cycle/period calculates predicted vs actual date, returns daysEarly (negative) or daysLate (positive)
|
|
- ICS feed shows "(Predicted)" suffix on menstruation events when actual date differs from predicted date
|
|
- Calendar route fetches all period logs and passes them to ICS generator for prediction feedback
|
|
- **Why:** Creates feedback loop for understanding cycle prediction accuracy per calendar.md spec
|
|
|
|
### P4.6: Rate Limiting ✅ COMPLETE
|
|
- [x] Login attempt rate limiting
|
|
- **Spec Reference:** specs/email.md mentions 5 login attempts per minute
|
|
- **Files:**
|
|
- `src/app/login/page.tsx` - Added client-side rate limiting with 5 attempts per minute
|
|
- **Tests:**
|
|
- `src/app/login/page.test.tsx` - 6 new tests (32 total) covering rate limiting
|
|
- **Features Implemented:**
|
|
- Client-side rate limiting tracking login attempts with timestamps
|
|
- Rate limit: 5 attempts per minute (RATE_LIMIT_WINDOW_MS = 60000)
|
|
- Shows "Too many login attempts. Please try again in 1 minute." error when rate limited
|
|
- Shows remaining attempts warning after 3 failures
|
|
- Disables form/button when rate limited
|
|
- Auto-clears after cooldown period
|
|
- Works for both email/password and OIDC login
|
|
- **Why:** Security requirement from spec
|
|
|
|
---
|
|
|
|
## Implementation Order
|
|
|
|
```
|
|
P0.1 PocketBase Auth ──┬──> P0.2 Auth Middleware ──> P0.4 GET /api/user
|
|
│
|
|
P0.3 Override Logic ───┴──> P1.4 GET /api/today ──> P1.7 Dashboard
|
|
|
|
P1.1 PATCH /api/user ────> P2.9 Settings Page
|
|
P1.2 POST period ────────> P1.3 GET current ────> P1.7 Dashboard
|
|
P1.5 Overrides API ──────> P1.7 Dashboard
|
|
P1.6 Login Page ─────────> P2.18 OIDC Auth (upgrade)
|
|
|
|
P2.1 Garmin fetchers ──> P2.2 Garmin tokens ──> P2.4 Cron sync ──> P2.5 Notifications
|
|
│
|
|
└──> P3.9 Token Warnings
|
|
P2.3 Garmin status ────> P2.10 Garmin settings
|
|
|
|
P2.6 ICS endpoint ─────> P2.11 Calendar page
|
|
P2.7 Regen token
|
|
|
|
P2.8 History API ──────> P2.12 History page
|
|
P2.13 Plan page
|
|
P2.14 Mini calendar
|
|
|
|
P2.15 Health endpoint (independent - HIGH PRIORITY for deployment)
|
|
P2.16 Metrics endpoint (independent)
|
|
P2.17 Structured logging (independent, but should be done before other items for proper logging)
|
|
|
|
P3.11 Component tests ─> Can be done in parallel with other work
|
|
P4.* UX Polish ────────> After core functionality complete
|
|
```
|
|
|
|
### Remaining Work Priority
|
|
|
|
| Priority | Task | Effort | Notes |
|
|
|----------|------|--------|-------|
|
|
| Done | P4.4 Loading Performance | Complete | Next.js loading.tsx provides 100ms target |
|
|
| Done | P4.5 Period Prediction | Complete | Prediction tracking with feedback loop |
|
|
| Done | P4.6 Rate Limiting | Complete | Client-side rate limiting implemented |
|
|
| Done | P5.1 Period History UI | Complete | Page + 3 API routes with 61 tests |
|
|
| Done | P5.3 CI Pipeline | Complete | Lint, typecheck, tests in Gitea Actions |
|
|
| Done | P5.4 E2E Tests | Complete | 64 tests across 6 files |
|
|
| Done | P5.2 Toast Notifications | Complete | sonner library + 23 tests |
|
|
|
|
**All P0-P5 items are complete. The project is feature complete.**
|
|
|
|
|
|
|
|
### Dependency Summary
|
|
|
|
| Task | Blocked By | Blocks |
|
|
|------|------------|--------|
|
|
| P0.1 | - | P0.2, P0.4, P1.1-P1.6, P2.2-P2.3, P2.7-P2.8 |
|
|
| P0.2 | P0.1 | P0.4, P1.1-P1.5, P2.2-P2.3, P2.7-P2.8 |
|
|
| P0.3 | - | P1.4, P1.5 |
|
|
| P0.4 | P0.1, P0.2 | P1.7, P2.9, P2.10, P2.13 |
|
|
| P3.9 | P2.4 | - |
|
|
|
|
---
|
|
|
|
## Completed
|
|
|
|
### Library
|
|
- [x] **cycle.ts** - Complete with 22 tests (`getCycleDay`, `getPhase` with dynamic boundaries for variable cycle lengths, `getPhaseConfig`, `getPhaseLimit`)
|
|
- [x] **decision-engine.ts** - Complete with 24 tests (`getTrainingDecision` + `getDecisionWithOverrides`)
|
|
- [x] **pocketbase.ts** - Complete with 9 tests (`createPocketBaseClient`, `isAuthenticated`, `getCurrentUser`, `loadAuthFromCookies`)
|
|
- [x] **nutrition.ts** - Complete with 17 tests (`getNutritionGuidance`, `getSeedSwitchAlert`, phase-specific carb ranges, keto guidance) (P3.2)
|
|
- [x] **email.ts** - Complete with 30 tests (`sendDailyEmail`, `sendPeriodConfirmationEmail`, `sendTokenExpirationWarning`, email formatting, structured logging for sent/failed events) (P3.3, P3.9)
|
|
- [x] **ics.ts** - Complete with 33 tests (`generateIcsFeed`, ICS format validation, 90-day event generation, period prediction feedback, CATEGORIES for calendar colors) (P3.4, P4.5)
|
|
- [x] **encryption.ts** - Complete with 14 tests (AES-256-GCM encrypt/decrypt, round-trip validation, error handling) (P3.5)
|
|
- [x] **garmin.ts** - Complete with 33 tests (`fetchGarminData`, `fetchHrvStatus`, `fetchBodyBattery`, `fetchIntensityMinutes`, `isTokenExpired`, `daysUntilExpiry`, error handling) (P2.1, P3.6)
|
|
- [x] **auth-middleware.ts** - Complete with 6 tests (`withAuth()` wrapper)
|
|
- [x] **middleware.ts** - Complete with 12 tests (Next.js page protection)
|
|
- [x] **logger.ts** - Complete with 16 tests (JSON output, log levels, error serialization, child loggers) (P2.17)
|
|
- [x] **metrics.ts** - Complete with 18 tests (metrics collection, counters, gauges, histograms, Prometheus format) (P2.16)
|
|
|
|
### Components
|
|
- [x] **DecisionCard** - Displays decision status, icon, reason with color-coded backgrounds (RED/YELLOW/GREEN per status)
|
|
- [x] **DataPanel** - Shows body battery, HRV, intensity data
|
|
- [x] **NutritionPanel** - Shows seeds, carbs, keto guidance
|
|
- [x] **OverrideToggles** - Toggle buttons for flare/stress/sleep/pms
|
|
- [x] **DayCell** - Phase-colored calendar day cell with click handler
|
|
- [x] **MonthView** - Calendar grid with DayCell integration, navigation controls (prev/next month, Today button), phase legend, 21 tests
|
|
- [x] **MiniCalendar** - Compact calendar widget with phase colors, navigation, legend, 23 tests (P2.14)
|
|
|
|
### API Routes (21 complete)
|
|
- [x] **POST /api/auth/logout** - Clears session cookie, logs user out, 5 tests
|
|
- [x] **GET /api/user** - Returns authenticated user profile, 4 tests (P0.4)
|
|
- [x] **PATCH /api/user** - Updates user profile (cycleLength, notificationTime, timezone), 17 tests (P1.1)
|
|
- [x] **POST /api/cycle/period** - Logs period start date, updates user, creates PeriodLog with prediction tracking, 13 tests (P1.2, P4.5)
|
|
- [x] **GET /api/cycle/current** - Returns cycle day, phase, phaseConfig, daysUntilNextPhase, cycleLength, 10 tests (P1.3)
|
|
- [x] **GET /api/today** - Returns complete daily snapshot with decision, biometrics, nutrition, 22 tests (P1.4)
|
|
- [x] **POST /api/overrides** - Adds override to user.activeOverrides array, 14 tests (P1.5)
|
|
- [x] **DELETE /api/overrides** - Removes override from user.activeOverrides array, 14 tests (P1.5)
|
|
- [x] **POST /api/garmin/tokens** - Stores encrypted Garmin OAuth tokens, 15 tests (P2.2)
|
|
- [x] **DELETE /api/garmin/tokens** - Clears tokens and disconnects Garmin, 15 tests (P2.2)
|
|
- [x] **GET /api/garmin/status** - Returns connection status, expiry, warning level, 11 tests (P2.3)
|
|
- [x] **POST /api/cron/garmin-sync** - Daily sync of Garmin data for all connected users, creates DailyLogs, sends token expiration warnings, 32 tests (P2.4, P3.9)
|
|
- [x] **POST /api/cron/notifications** - Sends daily email notifications with timezone matching, DailyLog handling, nutrition guidance, 20 tests (P2.5)
|
|
- [x] **GET /api/calendar/[userId]/[token].ics** - Returns ICS feed with 90-day phase events and period prediction feedback, token validation, caching headers, 11 tests (P2.6, P4.5)
|
|
- [x] **POST /api/calendar/regenerate-token** - Generates new 32-char calendar token, returns URL, 9 tests (P2.7)
|
|
- [x] **GET /api/history** - Paginated historical daily logs with date filtering, validation, 19 tests (P2.8)
|
|
- [x] **GET /api/period-history** - Paginated period logs with cycle length calculation and average, 18 tests (P5.1)
|
|
- [x] **PATCH /api/period-logs/[id]** - Edit period log startDate with validation, 10 tests (P5.1)
|
|
- [x] **DELETE /api/period-logs/[id]** - Delete period log with user.lastPeriodDate update, 6 tests (P5.1)
|
|
- [x] **GET /api/health** - Health check endpoint with PocketBase connectivity check, 14 tests (P2.15)
|
|
- [x] **GET /metrics** - Prometheus metrics endpoint with counters, gauges, histograms, 33 tests (18 lib + 15 route) (P2.16)
|
|
|
|
### Pages (8 complete)
|
|
- [x] **Login Page** - OIDC (Pocket-ID) with email/password fallback, error handling, loading states, redirect, rate limiting, 32 tests (P1.6, P2.18, P4.6)
|
|
- [x] **Dashboard Page** - Complete daily interface with /api/today integration, DecisionCard, DataPanel, NutritionPanel, OverrideToggles, 23 tests (P1.7)
|
|
- [x] **Settings Page** - Form for cycleLength, notificationTime, timezone with validation, loading states, error handling, logout button, 34 tests (P2.9)
|
|
- [x] **Settings/Garmin Page** - Token input form, connection status, expiry warnings, disconnect functionality, 27 tests (P2.10)
|
|
- [x] **Calendar Page** - MonthView with navigation controls, ICS subscription section with URL display, copy button, token regeneration, 23 tests (P2.11)
|
|
- [x] **History Page** - Table view of DailyLogs with date filtering, pagination, decision styling, 26 tests (P2.12)
|
|
- [x] **Period History Page** - Table view of PeriodLogs with edit/delete modals, pagination, cycle length calculation, average cycle length, prediction accuracy display, 27 tests (P5.1)
|
|
- [x] **Plan Page** - Phase overview, training guidance, exercise reference, rebounding techniques, 16 tests (P2.13)
|
|
|
|
### Test Infrastructure
|
|
- [x] **test-setup.ts** - Global test setup with @testing-library/jest-dom matchers and cleanup
|
|
|
|
### P3: Quality and Testing
|
|
- [x] **P3.1: Decision Engine Tests** - Complete with 24 tests covering all 8 priority rules and override combinations
|
|
- [x] **P3.2: Nutrition Tests** - Complete with 17 tests covering seed cycling, carb ranges, keto guidance by phase
|
|
- [x] **P3.3: Email Tests** - Complete with 30 tests covering daily emails, period confirmation, token expiration warnings, structured logging
|
|
- [x] **P3.4: ICS Tests** - Complete with 28 tests covering ICS format validation, 90-day event generation, timezone handling, period prediction feedback
|
|
- [x] **P3.5: Encryption Tests** - Complete with 14 tests covering AES-256-GCM round-trip, error handling, key validation
|
|
- [x] **P3.6: Garmin Tests** - Complete with 33 tests covering API interactions, token expiry, error handling
|
|
- [x] **P3.7: Error Handling Improvements** - Replaced console.error with structured pino logger across API routes, added key event logging (Period logged, Override toggled, Decision calculated, Auth failure), 3 new tests in auth-middleware.test.ts
|
|
- [x] **P3.8: Loading States** - Complete with skeleton components (DecisionCardSkeleton, DataPanelSkeleton, NutritionPanelSkeleton, MiniCalendarSkeleton, OverrideTogglesSkeleton, CycleInfoSkeleton, DashboardSkeleton), 29 tests in skeletons.test.tsx; loading.tsx files for all routes (dashboard, calendar, history, plan, settings); shimmer animations matching spec requirements
|
|
- [x] **P3.9: Token Expiration Warnings** - Complete with 10 new tests in email.test.ts, 10 new tests in garmin-sync/route.test.ts; sends warnings at 14 and 7 days before expiry
|
|
- [x] **P3.11: Missing Component Tests** - Complete with 90 tests across 5 component test files (DecisionCard: 19, DataPanel: 18, NutritionPanel: 12, OverrideToggles: 18, DayCell: 23)
|
|
|
|
### P4: UX Polish and Accessibility
|
|
- [x] **P4.1: Dashboard Onboarding Banners** - Complete with OnboardingBanner component (16 tests), dashboard integration (5 new tests)
|
|
- [x] **P4.2: Accessibility Improvements** - Complete with skip navigation, semantic landmarks, calendar screen reader labels, keyboard navigation (9 new tests)
|
|
- [x] **P4.3: Dark Mode Configuration** - Complete with automatic dark mode via prefers-color-scheme media query
|
|
- [x] **P4.4: Loading Performance** - Complete with Next.js loading.tsx providing 100ms target, skeleton components, and optimistic UI for overrides
|
|
- [x] **P4.5: Period Prediction Accuracy Feedback** - Complete with predictedDate tracking in PeriodLog, daysEarly/daysLate calculations, "(Predicted)" labels in ICS feed (11 new tests across 3 files)
|
|
- [x] **P4.6: Rate Limiting** - Complete with client-side login rate limiting (5 attempts per minute, 6 new tests)
|
|
|
|
---
|
|
|
|
## Discovered Issues
|
|
|
|
*Bugs and inconsistencies found during implementation*
|
|
|
|
### Spec Gaps Discovered (2025-01-11)
|
|
Analysis of all specs vs implementation revealed these gaps:
|
|
|
|
| Gap | Spec | Status | Notes |
|
|
|-----|------|--------|-------|
|
|
| Logout functionality | authentication.md | **COMPLETE** | Added POST /api/auth/logout + settings button |
|
|
| Garmin sync structured logging | observability.md | **COMPLETE** | Added sync start/complete/failure logging |
|
|
| Email sent/failed logging | observability.md | **COMPLETE** | Email events now logged (info for success, error for failure) with structured data (userId, emailType, success) |
|
|
| Period history UI | cycle-tracking.md | **COMPLETE** | See P5.1 below |
|
|
| Dashboard color-coded backgrounds | dashboard.md | **COMPLETE** | DecisionCard shows RED/YELLOW/GREEN backgrounds per status (8 new tests) |
|
|
| Toast notifications | dashboard.md | **COMPLETE** | sonner library + Toaster component + showToast utility (23 tests) |
|
|
| CI pipeline | testing.md | **COMPLETE** | See P5.3 below |
|
|
|
|
---
|
|
|
|
## P5: Final Items ✅ ALL COMPLETE
|
|
|
|
These items were identified during gap analysis and have been completed.
|
|
|
|
### P5.1: Period History UI ✅ COMPLETE
|
|
- [x] Create period history viewing and editing UI
|
|
- **Spec Reference:** specs/cycle-tracking.md lines 93-111
|
|
- **Implementation Details:**
|
|
- **GET /api/period-history** - Paginated list of period logs with cycle length calculations (18 tests)
|
|
- Returns items with startDate, id, cycleLength (days since previous period)
|
|
- Calculates average cycle length across all periods
|
|
- Includes prediction accuracy metrics (totalPeriods, predictedCorrectly)
|
|
- Pagination with page/limit/totalPages/hasMore
|
|
- Sorts by startDate descending (most recent first)
|
|
- **PATCH /api/period-logs/[id]** - Edit period log startDate (10 tests)
|
|
- Validates startDate in YYYY-MM-DD format
|
|
- Prevents duplicate period dates
|
|
- Updates associated PeriodLog record
|
|
- Returns updated period log
|
|
- **DELETE /api/period-logs/[id]** - Delete period log (6 tests)
|
|
- Updates user.lastPeriodDate to most recent remaining period
|
|
- Handles deletion of last period log (sets lastPeriodDate to null)
|
|
- Requires authentication
|
|
- Returns 204 No Content on success
|
|
- **/period-history page** - Table view with edit/delete modals (27 tests)
|
|
- Table columns: Date, Cycle Length, Days Early/Late, Actions (Edit/Delete)
|
|
- Edit modal with date input and validation
|
|
- Delete confirmation modal with warning text
|
|
- Pagination controls with page numbers
|
|
- Displays average cycle length at top
|
|
- Shows prediction accuracy percentage
|
|
- Loading states and error handling
|
|
- **Files Created:**
|
|
- `src/app/api/period-history/route.ts` - API route with 18 tests
|
|
- `src/app/api/period-logs/[id]/route.ts` - API route with 16 tests (10 PATCH, 6 DELETE)
|
|
- `src/app/period-history/page.tsx` - Page component with 27 tests
|
|
- **Total Tests Added:** 61 tests (18 + 16 + 27)
|
|
- **Why:** Users need to view and correct their period log history per spec requirement
|
|
|
|
### P5.2: Toast Notifications ✅ COMPLETE
|
|
- [x] Add toast notification system for user feedback
|
|
- **Spec Reference:** specs/dashboard.md lines 88-96
|
|
- **Features Implemented:**
|
|
- Toast position: Bottom-right
|
|
- Auto-dismiss after 5 seconds for success/info toasts
|
|
- Error toasts persist until dismissed (per spec: "Errors persist until dismissed")
|
|
- Toast types: success, error, info
|
|
- Dark mode support with proper color theming
|
|
- Close button on all toasts
|
|
- **Library Used:** sonner (v2.0.7)
|
|
- **Files Created/Modified:**
|
|
- `src/components/ui/toaster.tsx` - Toaster component wrapping sonner with showToast utility (23 tests)
|
|
- `src/app/layout.tsx` - Added Toaster provider
|
|
- `src/app/page.tsx` - Dashboard override toggle errors now use toast
|
|
- `src/app/settings/page.tsx` - Settings save/load errors now use toast
|
|
- `src/app/settings/garmin/page.tsx` - Garmin connection success/error now use toast
|
|
- `src/app/calendar/page.tsx` - Calendar load errors now use toast
|
|
- `src/app/period-history/page.tsx` - Period history load/delete errors now use toast
|
|
- **Test Files Updated:**
|
|
- `src/components/ui/toaster.test.tsx` - 23 tests for toast component
|
|
- `src/app/layout.test.tsx` - Added Toaster mock
|
|
- `src/app/page.test.tsx` - Added showToast mock and test
|
|
- `src/app/settings/page.test.tsx` - Added showToast mock
|
|
- `src/app/settings/garmin/page.test.tsx` - Added showToast mock
|
|
- `src/app/calendar/page.test.tsx` - Added showToast mock and test
|
|
- `src/app/period-history/page.test.tsx` - Added showToast mock and tests
|
|
- **Total Tests Added:** 27 new tests (23 toaster + 4 integration tests across pages)
|
|
- **Why:** Better UX for transient feedback per spec requirements
|
|
|
|
### P5.3: CI Pipeline ✅ COMPLETE
|
|
- [x] Add test/lint/build to CI pipeline
|
|
- **Spec Reference:** specs/testing.md mentions CI pipeline
|
|
- **Required:** "All tests (unit, integration, E2E) pass in CI before merge"
|
|
- **Files Created:**
|
|
- `.gitea/workflows/ci.yml` - CI workflow with lint, typecheck, and unit tests
|
|
- **Features Implemented:**
|
|
- Runs on pull requests to main and pushes to main
|
|
- Node.js 24 with pnpm 10 setup
|
|
- pnpm dependency caching for faster CI runs
|
|
- Linting with `pnpm lint`
|
|
- Type checking with `pnpm tsc --noEmit`
|
|
- Unit tests with `pnpm test:run`
|
|
- Required environment variables provided for CI context
|
|
- **Why:** CI enforcement prevents broken code from being merged
|
|
|
|
### P5.4: E2E Tests ✅ COMPLETE
|
|
- [x] Complete E2E test suite for all user flows
|
|
- **Spec Reference:** specs/testing.md
|
|
- **Files Created:**
|
|
- `e2e/smoke.spec.ts` - 3 tests for basic app functionality
|
|
- `e2e/auth.spec.ts` - 14 tests for login page, protected routes, public routes
|
|
- `e2e/dashboard.spec.ts` - 10 tests for dashboard display and overrides
|
|
- `e2e/settings.spec.ts` - 15 tests for settings and Garmin configuration
|
|
- `e2e/period-logging.spec.ts` - 9 tests for period history and API auth
|
|
- `e2e/calendar.spec.ts` - 13 tests for calendar view and ICS endpoints
|
|
- **Total E2E Tests:** 64 tests (28 pass without auth, 36 skip when TEST_USER_EMAIL/TEST_USER_PASSWORD not set)
|
|
- **Test Categories:**
|
|
- Unauthenticated flows: Login page UI, form validation, error handling, protected route redirects
|
|
- Authenticated flows: Dashboard display, settings form, calendar navigation (requires test credentials)
|
|
- API endpoints: Health check, auth requirements for protected endpoints
|
|
- **Why:** Comprehensive E2E coverage ensures production reliability
|
|
|
|
### Previously Fixed Issues
|
|
|
|
- [x] ~~**CRITICAL: Cycle phase boundaries hardcoded for 31-day cycle**~~ - FIXED. Phase boundaries were not scaling with cycle length. The spec (cycle-tracking.md) defines formulas: MENSTRUAL 1-3, FOLLICULAR 4-(cycleLength-16), OVULATION (cycleLength-15)-(cycleLength-14), EARLY_LUTEAL (cycleLength-13)-(cycleLength-7), LATE_LUTEAL (cycleLength-6)-cycleLength. Added `getPhaseBoundaries(cycleLength)` function and updated `getPhase()` to accept cycleLength parameter. Updated all callers (API routes, components) to pass cycleLength. Added 13 new tests.
|
|
- [x] ~~ICS emojis did not match calendar.md spec~~ - FIXED. Changed from colored circles (🔵🟢🟣🟡🔴) to thematic emojis (🩸🌱🌸🌙🌑) per spec.
|
|
- [x] ~~ICS missing CATEGORIES field for calendar app colors~~ - FIXED. Added CATEGORIES field per calendar.md spec: MENSTRUAL=Red, FOLLICULAR=Green, OVULATION=Pink, EARLY_LUTEAL=Yellow, LATE_LUTEAL=Orange. Added 5 new tests.
|
|
- [x] ~~`src/lib/auth-middleware.ts` does not exist~~ - CREATED in P0.2
|
|
- [x] ~~`src/middleware.ts` does not exist~~ - CREATED in P0.2
|
|
- [x] ~~`garmin.ts` is only ~30% complete - missing specific biometric fetchers~~ - FIXED in P2.1 (added fetchHrvStatus, fetchBodyBattery, fetchIntensityMinutes)
|
|
- [x] ~~`pocketbase.ts` missing all auth helper functions~~ - FIXED in P0.1
|
|
- [x] ~~`src/app/api/today/route.ts` type error with null body battery values~~ - FIXED (added null coalescing)
|
|
- [x] Build requires RESEND_API_KEY and ENCRYPTION_KEY environment variables - works with `pnpm test:run` but `pnpm build` fails without them. This is expected behavior for production builds.
|
|
|
|
---
|
|
|
|
## Notes
|
|
|
|
1. **TDD Approach:** Each implementation task should follow TDD - write failing tests first, then implement
|
|
2. **Auth First:** P0 items unlock all other work; prioritize ruthlessly
|
|
3. **Incremental Delivery:** P1 completion = usable app without Garmin (manual data entry fallback)
|
|
4. **P2 Completion:** Full feature set with automation
|
|
5. **P3:** Quality and polish for production confidence
|
|
6. **P4:** UX polish and accessibility improvements from spec requirements
|
|
7. **Component Reuse:** Dashboard components are complete and can be used directly in P1.7
|
|
8. **HRV Rule:** HRV Unbalanced status ALWAYS forces REST - this is the highest algorithmic priority and cannot be overridden by manual toggles
|
|
9. **Override Order:** When multiple overrides are active, apply in order: flare > stress > sleep > pms
|
|
10. **Token Warnings:** Per spec, warnings are sent at exactly 14 days and 7 days before expiry (P3.9 COMPLETE)
|
|
11. **Health Check Priority:** P2.15 (GET /api/health) should be implemented early - it's required for deployment monitoring and load balancer health probes
|
|
12. **Structured Logging:** P2.17 (pino logger) is COMPLETE - new code should use `import { logger } from "@/lib/logger"` for all logging
|
|
13. **OIDC Authentication:** P2.18 COMPLETE - Login page auto-detects OIDC via `listAuthMethods()` and shows "Sign In with Pocket-ID" button when configured. Falls back to email/password when OIDC not available. Configure OIDC provider in PocketBase Admin under Settings → Auth providers → OpenID Connect
|
|
15. **Dark Mode:** COMPLETE - Auto-detects system preference via prefers-color-scheme media query (P4.3)
|
|
16. **Component Tests:** P3.11 COMPLETE - All 5 dashboard and calendar components now have comprehensive unit tests (90 tests total)
|
|
17. **Gap Analysis (2026-01-12):** Verified 889 tests across 46 files. All API routes (18), pages (7), components, and lib files (12) have tests. P0-P4 complete. Remaining gaps (Period History UI, Toast Notifications, CI Pipeline, E2E Tests) documented in P5.
|