Commit Graph

116 Commits

Author SHA1 Message Date
dbf0c32588 Fix garmin status showing stale connection state
All checks were successful
Deploy / deploy (push) Successful in 1m37s
Fetch fresh user data from database in status endpoint instead of
relying on auth store cookie, which may be stale after token save.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-12 21:45:35 +00:00
6bd5eb663b Add Playwright E2E testing infrastructure
- Add playwright-web-flake to flake.nix for NixOS browser support
- Pin @playwright/test@1.56.1 to match nixpkgs version
- Create playwright.config.ts with Chromium-only, auto-start dev server
- Add e2e/smoke.spec.ts with initial smoke tests
- Add .mcp.json for Claude browser control via MCP
- Update .gitignore for playwright artifacts
- Remove E2E test skip from spec.md Known Limitations
- Update specs/testing.md to require three-tier testing approach

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-12 21:43:24 +00:00
30c5955a61 Fix garmin token expires_at format for PocketBase compatibility
Output expires_at as ISO 8601 date string instead of Unix timestamp.
PocketBase date fields expect ISO format, and the integer was causing
token saves to fail silently.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-12 21:25:38 +00:00
ce80fb1ede Add database setup script and fix dark mode visibility
- Add scripts/setup-db.ts to programmatically create missing PocketBase
  collections (period_logs, dailyLogs) with proper relation fields
- Fix dark mode visibility across settings, login, calendar, and dashboard
  components by using semantic CSS tokens and dark: variants
- Add db:setup npm script and document usage in AGENTS.md
- Update vitest config to include scripts directory tests

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-12 21:23:20 +00:00
ca35b36efa Fix garth token serialization using TypeAdapter for Pydantic dataclasses
Garth's OAuth1Token and OAuth2Token are Pydantic dataclasses, not BaseModel
subclasses, so they require TypeAdapter for serialization instead of model_dump().
Also adds user-friendly error handling for authentication failures.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-12 17:45:01 +00:00
c8a738d0c4 Fix garth token serialization using Pydantic v2 API
All checks were successful
Deploy / deploy (push) Successful in 1m36s
The garth library uses Pydantic dataclasses for OAuth tokens which don't
have a serialize() method. Use model_dump() instead, and fix expires_at
handling since it's an integer timestamp not a datetime object.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-12 16:49:16 +00:00
2408839b8b Fix 404 error when saving user preferences
Routes using withAuth were creating new unauthenticated PocketBase
clients, causing 404 errors when trying to update records. Modified
withAuth to pass the authenticated pb client to handlers so they can
use it for database operations.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-12 16:45:55 +00:00
df2f52ad50 Install deps for python script. 2026-01-12 15:00:31 +00:00
0e585e6bb4 Add period date setup modal for new users
All checks were successful
Deploy / deploy (push) Successful in 2m27s
Users without a lastPeriodDate can now set it via a modal opened from
the onboarding banner. The dashboard now fetches user data independently
so the banner shows even when /api/today fails due to missing period date.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-12 14:28:49 +00:00
72706bb91b Fix Invalid Date error in auth middleware
All checks were successful
Deploy / deploy (push) Successful in 2m28s
Add parseDate helper that safely returns null for empty/invalid date
strings from PocketBase. This prevents RangeError when pino logger
tries to serialize Invalid Date objects via toISOString().

- Make garminTokenExpiresAt and lastPeriodDate nullable in User type
- Filter garmin-sync cron to skip users without required dates
- Add test assertions for null date handling

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-12 14:13:02 +00:00
63023550fd Symlink to agents.md 2026-01-12 14:01:02 +00:00
2ae6804cc4 Fix OAuth login by syncing auth state to cookie
All checks were successful
Deploy / deploy (push) Successful in 1m37s
Root cause: PocketBase SDK stores auth in localStorage, but Next.js
middleware checks for pb_auth cookie. The cookie was never being set
after successful OAuth login.

Fix: Add pb.authStore.onChange() listener that syncs auth state to
cookie on any change (login, logout, token refresh). This is the
idiomatic PocketBase pattern for Next.js SSR apps.

Also updates authentication spec to reflect that the cookie is
non-HttpOnly by design (client SDK needs read/write access).

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-12 13:59:53 +00:00
e2afee2045 Add OAuth debugging console.log statements
All checks were successful
Deploy / deploy (push) Successful in 1m38s
Temporary debugging to diagnose why authWithOAuth2 promise
doesn't resolve after successful code exchange.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-12 13:49:02 +00:00
5cac8f3267 Add color-coded backgrounds to DecisionCard
All checks were successful
Deploy / deploy (push) Successful in 2m26s
Per dashboard.md spec requirements:
- RED background and text for REST decisions
- YELLOW background and text for GENTLE/LIGHT/REDUCED decisions
- GREEN background and text for TRAIN decisions

Added 8 new tests for color-coded backgrounds (19 total).
Updated IMPLEMENTATION_PLAN.md to mark spec gap as complete.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-11 23:11:10 +00:00
31932a88bf Add email sent/failed structured logging
All checks were successful
Deploy / deploy (push) Successful in 1m38s
Implement email logging per observability spec:
- Add structured logging for email sent (info level) and failed (error level)
- Include userId, type, and recipient fields in log events
- Add userId parameter to email functions (sendDailyEmail, sendPeriodConfirmationEmail, sendTokenExpirationWarning)
- Update cron routes (notifications, garmin-sync) to pass userId

6 new tests added to email.test.ts (now 30 tests total)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-11 23:06:19 +00:00
13b58c3c32 Add logout functionality and Garmin sync structured logging
- Add POST /api/auth/logout endpoint with tests (5 tests)
- Add logout button to settings page (5 tests)
- Add structured logging to garmin-sync cron (sync start/complete/failure)
- Update IMPLEMENTATION_PLAN.md with spec gap analysis findings
- Total: 835 tests passing across 44 test files

Closes spec gaps from authentication.md (logout) and observability.md (logging)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-11 23:00:54 +00:00
e9a77fd79c Mark P4.4 Loading Performance as complete
All checks were successful
Deploy / deploy (push) Successful in 2m35s
Verified that loading performance requirements are met:
- Next.js loading.tsx files render immediately during navigation (< 100ms)
- Skeleton components exist for all routes (P3.8 already complete)
- Optimistic UI updates implemented for override toggles
- Suspense boundaries provided by Next.js App Router

All P4 UX Polish items are now complete.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-11 22:44:50 +00:00
a977934c23 Fix critical bug: cycle phase boundaries now scale with cycle length
CRITICAL BUG FIX:
- Phase boundaries were hardcoded for 31-day cycle, breaking correct
  phase calculations for users with different cycle lengths (28, 35, etc.)
- Added getPhaseBoundaries(cycleLength) function in cycle.ts
- Updated getPhase() to accept cycleLength parameter (default 31)
- Updated all callers (API routes, components) to pass cycleLength
- Added 13 new tests for phase boundaries with 28, 31, and 35-day cycles

ICS IMPROVEMENTS:
- Fixed emojis to match calendar.md spec: 🩸🌱🌸🌙🌑
- Added CATEGORIES field for calendar app colors per spec:
  MENSTRUAL=Red, FOLLICULAR=Green, OVULATION=Pink,
  EARLY_LUTEAL=Yellow, LATE_LUTEAL=Orange
- Added 5 new tests for CATEGORIES

Updated IMPLEMENTATION_PLAN.md with discovered issues and test counts.

825 tests passing (up from 807)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-11 22:39:09 +00:00
58f6c5605a Add period prediction accuracy feedback (P4.5 complete)
All checks were successful
Deploy / deploy (push) Successful in 1m36s
Implements visual feedback for cycle prediction accuracy in ICS calendar feeds:

- Add predictedDate field to PeriodLog type for tracking predicted vs actual dates
- POST /api/cycle/period now calculates and stores predictedDate based on
  previous lastPeriodDate + cycleLength, returns daysEarly/daysLate in response
- ICS feed generates "(Predicted)" events when actual period start differs
  from predicted, with descriptions like "period arrived 2 days early"
- Calendar route fetches period logs and passes them to ICS generator

This creates an accuracy feedback loop helping users understand their cycle
variability over time per calendar.md spec.

807 tests passing across 43 test files.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-11 22:21:52 +00:00
c708c2ed8b Add login rate limiting (P4.6 complete)
All checks were successful
Deploy / deploy (push) Successful in 1m38s
Implement client-side rate limiting for login page with 5 attempts
per minute, matching the spec requirement in authentication.md.

Features:
- Track login attempts with timestamps in component state
- Block login when 5+ attempts made within 60 seconds
- Show "Too many login attempts" error when rate limited
- Show remaining attempts warning after 3 failures
- Disable form/button when rate limited
- Auto-clear after 1 minute cooldown
- Works for both email/password and OIDC authentication

Tests:
- 6 new tests covering rate limiting scenarios (32 total)
- 796 tests passing across 43 test files

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-11 22:09:34 +00:00
2e7d8dc4ca Add automatic dark mode via prefers-color-scheme (P4.3 complete)
All checks were successful
Deploy / deploy (push) Successful in 1m36s
Switch from class-based dark mode to automatic system preference
detection using CSS prefers-color-scheme media query. The app now
respects the user's OS-level dark mode setting without requiring
a manual toggle, as specified in the dashboard requirements.

Changes:
- Update Tailwind custom variant to use @media (prefers-color-scheme: dark)
- Change .dark selector to media query wrapping :root variables
- No component changes needed - existing CSS variable system handles theming

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-11 22:00:50 +00:00
4015f1ba3a Add calendar keyboard navigation (P4.2 complete)
All checks were successful
Deploy / deploy (push) Successful in 2m26s
Implement keyboard navigation for MonthView calendar:
- ArrowLeft/Right: navigate to previous/next day
- ArrowUp/Down: navigate to previous/next week (7 days)
- Home/End: navigate to first/last day of month
- Boundary navigation triggers month change

Features:
- Added role="grid" for proper ARIA semantics
- Added data-day attribute to DayCell for focus management
- Wrapped navigation handlers in useCallback for stability

Tests: 9 new tests for keyboard navigation (790 total)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-11 21:55:18 +00:00
649fa29df2 Add accessibility improvements (P4.2 partial)
All checks were successful
Deploy / deploy (push) Successful in 1m36s
- Add skip navigation link to root layout
- Add semantic HTML landmarks (main element) to login and settings pages
- Add aria-labels to calendar day buttons with date, cycle day, and phase info
- Add id="main-content" to dashboard main element for skip link target
- Fix pre-existing type error in auth-middleware.test.ts

Tests: 781 passing (11 new accessibility tests)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-11 21:49:26 +00:00
2bfd93589b Add dashboard onboarding banners (P4.1)
All checks were successful
Deploy / deploy (push) Successful in 2m29s
Implement OnboardingBanner component that prompts new users to complete
setup with contextual banners for:
- Garmin connection (links to /settings/garmin)
- Period date (button with callback for date picker)
- Notification time (links to /settings)

Banners display at the top of the dashboard when setup is incomplete,
with icons and styled action buttons. Each banner uses role="alert"
for accessibility.

- Add OnboardingBanner component (16 tests)
- Integrate into dashboard page (5 new tests, 28 total)
- Update UserData interface to include garminConnected, notificationTime
- Test count: 770 tests across 43 files

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-11 21:38:16 +00:00
f69e1fd614 Separate server-side and client-side PocketBase URLs
All checks were successful
Deploy / deploy (push) Successful in 2m29s
Server-side code (health checks, API routes) needs to use internal
sidecar URL (POCKETBASE_URL at runtime), while client-side needs
public URL (NEXT_PUBLIC_POCKETBASE_URL baked at build time).

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-11 21:17:58 +00:00
9db8051e8d Set production PocketBase URL in Docker build
Some checks failed
Deploy / deploy (push) Failing after 1m40s
NEXT_PUBLIC_* vars are baked in at build time, not runtime. Updated
docker.nix to use production URL and added deployment config location
to AGENTS.md.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-11 18:50:31 +00:00
3567fbafd7 Expose PocketBase URL to client-side for OIDC auth
Some checks failed
Deploy / deploy (push) Failing after 6m37s
POCKETBASE_URL was only available server-side, causing the login page
to fall back to localhost:8090 in the browser. Renamed to
NEXT_PUBLIC_POCKETBASE_URL so Next.js bundles it into client code.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-11 17:33:52 +00:00
6ebd9788e1 Fix docker extraCommands to copy hidden files
All checks were successful
Deploy / deploy (push) Successful in 1m36s
Same issue as installPhase - need to use /. suffix instead of /* glob
to include hidden directories like .next.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-11 17:06:28 +00:00
f2794cf396 Fix docker.nix to copy hidden .next directory
Some checks failed
Deploy / deploy (push) Failing after 1m35s
The glob * doesn't include hidden files/directories. Use /. suffix
to copy all contents including .next from standalone output.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-11 13:17:34 +00:00
8f643c299d Add Nomad deployment configuration and CI/CD pipeline
Some checks failed
Deploy / deploy (push) Failing after 1m43s
- Add docker.nix for Nix-based Docker image builds
- Update flake.nix with dockerImage package output
- Add output: standalone to next.config.ts for production builds
- Add /metrics endpoint for Prometheus scraping
- Add Gitea Actions workflow calling shared deploy-nomad.yaml

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-11 10:20:20 +00:00
9c5b8466f6 Implement skeleton loading states for dashboard and routes (P3.8)
Add skeleton loading components per specs/dashboard.md requirements:
- DecisionCardSkeleton: Shimmer placeholder for status and reason
- DataPanelSkeleton: Skeleton rows for 5 metrics
- NutritionPanelSkeleton: Skeleton for nutrition guidance
- MiniCalendarSkeleton: Placeholder grid with navigation and legend
- OverrideTogglesSkeleton: 4 toggle placeholders
- CycleInfoSkeleton: Cycle day and phase placeholders
- DashboardSkeleton: Combined skeleton for route-level loading

Add Next.js loading.tsx files for instant loading states:
- src/app/loading.tsx (Dashboard)
- src/app/calendar/loading.tsx
- src/app/history/loading.tsx
- src/app/plan/loading.tsx
- src/app/settings/loading.tsx

Update dashboard page to use DashboardSkeleton instead of "Loading..." text.

Fix flaky garmin test with wider date tolerance for timezone variations.

29 new tests in skeletons.test.tsx (749 total tests passing).

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-11 09:32:09 +00:00
714194f2d3 Implement structured logging for API routes (P3.7)
Replace console.error with pino structured logger across API routes
and add key event logging per observability spec:
- Auth failure (warn): reason
- Period logged (info): userId, date
- Override toggled (info): userId, override, enabled
- Decision calculated (info): userId, decision, reason
- Error events (error): err object with stack trace

Files updated:
- auth-middleware.ts: Added structured logging for auth failures
- cycle/period/route.ts: Added Period logged event + error logging
- calendar/[userId]/[token].ics/route.ts: Replaced console.error
- overrides/route.ts: Added Override toggled events
- today/route.ts: Added Decision calculated event

Tests: 720 passing (added 3 new structured logging tests)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-11 09:19:55 +00:00
00d902a396 Implement OIDC authentication with Pocket-ID (P2.18)
Add OIDC/OAuth2 authentication support to the login page with automatic
provider detection and email/password fallback.

Features:
- Auto-detect OIDC provider via PocketBase listAuthMethods() API
- Display "Sign In with Pocket-ID" button when OIDC is configured
- Use PocketBase authWithOAuth2() popup-based OAuth2 flow
- Fall back to email/password form when OIDC not available
- Loading states during authentication
- Error handling with user-friendly messages

The implementation checks for available auth methods on page load and
conditionally renders either the OIDC button or the email/password form.
This allows production deployments to use OIDC while development
environments can continue using email/password.

Tests: 24 tests (10 new OIDC tests added)
- OIDC button rendering when provider configured
- OIDC authentication flow with authWithOAuth2
- Loading and error states for OIDC
- Fallback to email/password when OIDC unavailable

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-11 09:12:29 +00:00
267d45f98a Add component tests for P3.11 (82 tests across 5 files)
- DecisionCard tests: 11 tests covering rendering, status icons, styling
- DataPanel tests: 18 tests covering biometrics display, null handling, styling
- NutritionPanel tests: 12 tests covering seeds, carbs, keto guidance display
- OverrideToggles tests: 18 tests covering toggle states, callbacks, styling
- DayCell tests: 23 tests covering phase coloring, today highlighting, click handling

Total tests now: 707 passing across 40 test files

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-11 09:02:34 +00:00
39198fdf8c Implement Plan page with phase overview and exercise reference (P2.13)
Add comprehensive training plan reference page that displays:
- Current phase status (day, phase name, training type, weekly limit)
- Phase overview cards for all 5 cycle phases with weekly intensity limits
- Strength training exercises reference with sets and reps
- Rebounding techniques organized by phase
- Weekly training guidelines for each phase

The page fetches cycle data from /api/cycle/current and highlights
the current phase. Implements full TDD with 16 tests covering loading
states, error handling, phase display, and exercise reference sections.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-11 08:56:52 +00:00
b2915bca9c Implement MiniCalendar dashboard widget (P2.14)
Complete the MiniCalendar component with:
- Full calendar grid showing all days of the month
- Phase colors applied to each day
- Today highlighting with ring indicator
- Navigation buttons (prev/next month, Today)
- Compact phase legend
- Integration into dashboard page (shows when lastPeriodDate exists)

Adds 23 new tests for the MiniCalendar component covering:
- Calendar grid rendering
- Phase color application
- Navigation functionality
- Cycle rollover handling
- Custom year/month props

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-11 08:47:28 +00:00
5a0cdf7450 Implement Prometheus metrics endpoint (P2.16)
Add comprehensive metrics collection for production monitoring:
- src/lib/metrics.ts: prom-client based metrics library with custom counters,
  gauges, and histograms for Garmin sync, email, and decision engine
- GET /api/metrics: Prometheus-format endpoint for scraping
- Integration into garmin-sync cron: sync duration, success/failure counts,
  active users gauge
- Integration into email.ts: daily and warning email counters
- Integration into decision-engine.ts: decision type counters

Custom metrics implemented:
- phaseflow_garmin_sync_total (counter with status label)
- phaseflow_garmin_sync_duration_seconds (histogram)
- phaseflow_email_sent_total (counter with type label)
- phaseflow_decision_engine_calls_total (counter with decision label)
- phaseflow_active_users (gauge)

33 new tests (18 library + 15 route), bringing total to 586 tests.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-11 08:40:42 +00:00
5ec3aba8b3 Implement structured logging with pino (P2.17)
Add pino-based logger module for production observability:
- JSON output to stdout for log aggregators (Loki, ELK)
- Configurable via LOG_LEVEL environment variable (defaults to "info")
- Log levels: error, warn, info, debug
- Error objects serialized with type, message, and stack trace
- Child logger support for bound context
- ISO 8601 timestamps in all log entries

Test coverage: 16 tests covering JSON format, log levels, error
serialization, and child loggers.

Total tests now: 553 passing across 31 test files.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-11 08:29:31 +00:00
2ffee63a59 Implement token expiration warnings (P3.9)
Add email warnings for Garmin token expiration at 14-day and 7-day thresholds.
When the garmin-sync cron job runs, it now checks each user's token expiry and
sends a warning email at exactly 14 days and 7 days before expiration.

Changes:
- Add sendTokenExpirationWarning() to email.ts with differentiated subject
  lines and urgency levels for 14-day vs 7-day warnings
- Integrate warning logic into garmin-sync cron route using daysUntilExpiry()
- Track warnings sent in sync response with new warningsSent counter
- Add 20 new tests (10 for email function, 10 for sync integration)

Test count: 517 → 537

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-11 08:24:19 +00:00
6c3dd34412 Implement health check endpoint (P2.15)
Add GET /api/health endpoint for deployment monitoring and load balancer
health probes. Returns 200 with status "ok" when PocketBase is reachable,
503 with status "unhealthy" when PocketBase connection fails.

Response includes timestamp (ISO 8601), version, and error message (on failure).
Uses PocketBase SDK's built-in health.check() method for connectivity testing.

14 tests covering healthy/unhealthy states and edge cases.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-11 08:17:13 +00:00
6a8d55c0b9 Document spec gaps: auth, phase scaling, observability, testing
Address 21 previously undefined behaviors across specs:

- Authentication: Replace email/password with OIDC (Pocket-ID)
- Cycle tracking: Add fixed-luteal phase scaling formula with examples
- Calendar: Document period logging behavior (preserve predictions)
- Garmin: Clarify connection is required (no phase-only mode)
- Dashboard: Add UI states, dark mode, onboarding, accessibility
- Notifications: Document timezone batching approach
- New specs: observability.md (health, metrics, logging)
- New specs: testing.md (unit + integration strategy)
- Main spec: Add backup/recovery, known limitations, API updates

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-11 07:49:56 +00:00
97a424e41d Implement Calendar page with MonthView and ICS subscription (P2.11)
- Complete MonthView component with calendar grid, DayCell integration,
  navigation controls (prev/next month, Today button), and phase legend
- Implement Calendar page with MonthView, month navigation state,
  ICS subscription section with URL display, copy, and token regeneration
- Add 21 tests for MonthView component (calendar grid, phase colors,
  navigation, legend, cycle rollover)
- Add 23 tests for Calendar page (rendering, navigation, ICS subscription,
  token regeneration, error handling)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-10 21:42:40 +00:00
742f220be5 Implement Garmin settings page with token management UI (P2.10)
- Add full Garmin connection management page at /settings/garmin
- Display connection status with colored indicators (green/red/gray)
- Show token expiry warnings (yellow 14 days, red 7 days)
- Token input form with JSON validation for bootstrap script output
- Disconnect functionality with confirmation
- Loading and error states throughout
- Add link from Settings page to Garmin settings
- 27 tests for Garmin settings page
- 3 additional tests for Settings page Garmin link

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-10 21:28:06 +00:00
be80c60253 Implement History page with table view and pagination (P2.12)
Add functional history page that displays DailyLog entries in a table
with date, cycle day/phase, decision, body battery, HRV, and intensity
columns. Features include:
- Data fetching from /api/history endpoint
- Pagination with previous/next navigation
- Date filtering with start/end date inputs
- Decision color coding (REST=red, TRAIN=green, GENTLE/LIGHT/REDUCED=yellow)
- Loading and error states
- Empty state when no history exists

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-10 20:22:08 +00:00
75f0e8ec80 Implement Settings page with form validation (P2.9)
- Add client-side form for cycleLength, notificationTime, timezone
- Fetch user data on mount and pre-fill form values
- Submit updates via PATCH /api/user with loading states
- Display success/error messages with proper accessibility
- Clear messages when user modifies form
- 24 tests covering rendering, data loading, validation, error handling

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-10 20:14:49 +00:00
e73d131450 Implement history API endpoint (P2.8)
Add GET /api/history for paginated historical daily logs with:
- Pagination support (page/limit query params, default 20 per page)
- Date filtering (startDate/endDate in YYYY-MM-DD format)
- Validation for all parameters with descriptive error messages
- Sort by date descending (most recent first)
- Response includes items, total, page, limit, totalPages, hasMore

Includes 19 tests covering pagination, date filtering, auth, and validation.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-10 20:06:50 +00:00
532d49f570 Implement calendar ICS feed and token regeneration (P2.6, P2.7)
Add two calendar-related API endpoints:

P2.6 - GET /api/calendar/[userId]/[token].ics:
- Token-based authentication (no session required)
- Validates calendar token against user record
- Generates 90 days of phase events using generateIcsFeed()
- Returns proper Content-Type and Cache-Control headers
- 404 for non-existent users, 401 for invalid tokens
- 10 tests covering all scenarios

P2.7 - POST /api/calendar/regenerate-token:
- 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
- 9 tests covering token generation and auth

Total: 19 new tests, 360 tests passing

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-10 20:02:07 +00:00
901543cb4d Implement notifications cron endpoint (P2.5)
Add daily email notification system that sends training decisions
at each user's preferred time in their timezone.

Features:
- Timezone-aware notification matching using Intl.DateTimeFormat
- DailyLog-based notifications with duplicate prevention
- Nutrition guidance integration via getNutritionGuidance
- Graceful error handling (continues processing on per-user failures)
- Summary response with detailed stats

Includes 20 tests covering:
- CRON_SECRET authentication
- Timezone matching (UTC and America/New_York)
- DailyLog existence and already-sent checks
- Email content assembly
- Error handling and response format

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-10 19:56:16 +00:00
fc970a2c61 Implement Garmin sync cron endpoint (P2.4)
Add daily sync functionality for Garmin biometric data:
- Fetch all users with garminConnected=true
- Skip users with expired tokens
- Decrypt OAuth2 tokens and fetch HRV, Body Battery, Intensity Minutes
- Calculate cycle day, phase, phase limit, remaining minutes
- Compute training decision using decision engine
- Create DailyLog entries for each user
- Return sync summary with usersProcessed, errors, skippedExpired, timestamp

Includes 22 tests covering:
- CRON_SECRET authentication
- User iteration and filtering
- Token decryption and expiry handling
- Garmin API data fetching
- DailyLog creation with all required fields
- Error handling and graceful degradation

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-10 19:50:26 +00:00
0fc25a49f1 Implement Garmin token management endpoints (P2.2, P2.3)
Added three Garmin API endpoints for token management:

- POST /api/garmin/tokens: Accepts oauth1, oauth2, expires_at;
  encrypts tokens using AES-256-GCM; stores in user record;
  returns daysUntilExpiry

- DELETE /api/garmin/tokens: Clears encrypted tokens from user
  record and sets garminConnected to false

- GET /api/garmin/status: Returns connection status, days until
  expiry, expired flag, and warning level (critical ≤7 days,
  warning 8-14 days)

All endpoints use withAuth() middleware for authentication.
Added 26 tests covering encryption, validation, auth, and
warning level thresholds.

Also added pb_data/ to .gitignore for PocketBase data.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-10 19:45:16 +00:00