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>
5.0 KiB
Dashboard Specification
Job to Be Done
When I open the app each morning, I want to immediately see whether I should train today, so that I can make informed decisions without manual data analysis.
Components
Decision Card (decision-card.tsx)
Displays the daily training decision prominently.
Required Elements:
- Large status indicator (REST / GENTLE / LIGHT / REDUCED / TRAIN)
- Color-coded background (red for REST, yellow for GENTLE/LIGHT/REDUCED, green for TRAIN)
- Reason text explaining why this decision was made
- Icon matching the decision status
Data Source:
/api/todayendpoint returns current decision
Data Panel (data-panel.tsx)
Shows the biometric data used to make the decision.
Required Elements:
- HRV Status (Balanced/Unbalanced/Unknown)
- Body Battery Current (0-100)
- Body Battery Yesterday Low (0-100)
- Cycle Day (e.g., "Day 12")
- Current Phase (MENSTRUAL, FOLLICULAR, OVULATION, EARLY_LUTEAL, LATE_LUTEAL)
- Week Intensity Minutes (accumulated)
- Phase Limit (weekly cap based on phase)
- Remaining Minutes until limit
Visual Indicators:
- Color-code HRV status (green=Balanced, red=Unbalanced, gray=Unknown)
- Progress bar for week intensity vs phase limit
Nutrition Panel (nutrition-panel.tsx)
Displays seed cycling and macro guidance.
Required Elements:
- Current seeds to consume (Flax+Pumpkin OR Sesame+Sunflower)
- Carb range for the day (e.g., "20-100g")
- Keto guidance (OPTIONAL / No / NEVER with reason)
- Seed switch alert on day 15
Data Source:
getNutritionGuidance(cycleDay)fromsrc/lib/nutrition.ts
Override Toggles (override-toggles.tsx)
Emergency modifications to the standard decision.
Override Types:
flare- Hashimoto's flare (force REST)stress- High stress day (force REST)sleep- Poor sleep (<6 hours) (force GENTLE)pms- PMS symptoms (force GENTLE)
Behavior:
- Toggle buttons for each override
- Active overrides persist until manually cleared
- Active overrides override the algorithmic decision
- Store in
user.activeOverrides[]
Mini Calendar (mini-calendar.tsx)
Visual cycle overview for the current month.
Required Elements:
- Monthly grid view
- Today highlighted
- Phase colors for each day
- Period days marked distinctly
- Quick navigation to previous/next month
UI States
Loading States
Use skeleton loaders during data fetching:
- Decision card: Shimmer placeholder for status and reason
- Data panel: Skeleton rows for each metric
- Mini calendar: Placeholder grid
Error Handling
Toast notifications for errors with actionable feedback:
- Network errors: "Unable to fetch data. Retry?"
- Garmin sync failed: "Garmin data unavailable. Using last known values."
- Save errors: "Failed to save. Try again."
Toast position: Bottom-right, auto-dismiss after 5 seconds (errors persist until dismissed).
Dark Mode
Auto-detect system preference via CSS prefers-color-scheme:
- No manual toggle
- Respect OS-level dark mode setting
- Ensure all phase colors maintain contrast in both modes
Onboarding
First-time users see inline prompts/banners for missing setup:
| Missing State | Banner Message | Action |
|---|---|---|
| Garmin not connected | "Connect your Garmin to get started" | Link to /settings/garmin |
| Last period not set | "Set your last period date for accurate tracking" | Opens date picker |
| Notification time not set | "Set your preferred notification time" | Link to /settings |
Banners appear at top of dashboard, dismissable once action completed.
Responsive Design
Mobile-responsive layout:
- Single-column layout on mobile (<768px)
- All critical metrics visible without scrolling
- Touch-friendly toggle buttons (min 44x44px tap targets)
- Collapsible sections for secondary content
Accessibility
Basic accessibility requirements:
- Semantic HTML (proper heading hierarchy, landmarks)
- Keyboard navigation for all interactive elements
- Focus indicators visible
- Color contrast minimum 4.5:1 for text
- Phase colors supplemented with icons/text (not color-only)
Success Criteria
- User can determine training status within 3 seconds of opening app
- All biometric data visible without scrolling on mobile
- Override toggles accessible but not accidentally activated
- Phase colors consistent across all components
- Dashboard usable with keyboard only
- Loading states display within 100ms of navigation
Acceptance Tests
- Dashboard loads decision from
/api/today - Decision card shows correct status and color
- Data panel displays all 8 required metrics
- Nutrition panel shows correct seeds for cycle day
- Override toggles update user record immediately
- Mini calendar renders current month with phase colors
- Skeleton loaders appear during data fetch
- Toast notifications display on error
- Dark mode activates based on system preference
- Onboarding banner shows when Garmin not connected
- Dashboard renders correctly on mobile viewport
- All interactive elements keyboard-accessible