feat: Implement husband features and fix iOS Safari web startup

Implement initial features for husband's companion app, including mock data
service and husband notes screen. Refactor scripture and cycle services
for improved stability and testability. Address iOS Safari web app
startup issue by removing deprecated initialization.

- Implemented MockDataService and HusbandNotesScreen.
- Converted _DashboardTab and DevotionalScreen to StatefulWidgets for robust
  scripture provider initialization.
- Refactored CycleService to use immutable CycleInfo class, reducing UI rebuilds.
- Removed deprecated window.flutterConfiguration from index.html, resolving
  Flutter web app startup failure on iOS Safari.
- Updated and fixed related tests.
This commit is contained in:
2025-12-26 22:40:52 -06:00
parent 464692ce56
commit b4b2bfe749
47 changed files with 240110 additions and 2578 deletions

View File

@@ -4,11 +4,7 @@ import 'dart:math' as math;
import '../theme/app_theme.dart';
import '../models/cycle_entry.dart';
<<<<<<< HEAD
class CycleRing extends StatelessWidget {
=======
class CycleRing extends StatefulWidget {
>>>>>>> 6742220 (Your commit message here)
final int dayOfCycle;
final int totalDays;
final CyclePhase phase;
@@ -21,77 +17,11 @@ class CycleRing extends StatefulWidget {
});
@override
<<<<<<< HEAD
Widget build(BuildContext context) {
final progress = dayOfCycle / totalDays;
final daysUntilNextPeriod = totalDays - dayOfCycle;
return Container(
width: 220,
height: 220,
child: Stack(
alignment: Alignment.center,
children: [
// Background ring
CustomPaint(
size: const Size(220, 220),
painter: _CycleRingPainter(
progress: progress,
phase: phase,
),
),
// Center content
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'Day $dayOfCycle',
style: GoogleFonts.outfit(
fontSize: 32,
fontWeight: FontWeight.w600,
color: AppColors.charcoal,
),
),
const SizedBox(height: 4),
Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
decoration: BoxDecoration(
color: _getPhaseColor(phase).withOpacity(0.2),
borderRadius: BorderRadius.circular(20),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Text(
phase.emoji,
style: const TextStyle(fontSize: 14),
),
const SizedBox(width: 6),
Text(
phase.label,
style: GoogleFonts.outfit(
fontSize: 14,
fontWeight: FontWeight.w500,
color: _getPhaseColor(phase),
),
),
],
),
),
const SizedBox(height: 8),
Text(
daysUntilNextPeriod > 0
? '$daysUntilNextPeriod days until period'
: 'Period expected',
style: GoogleFonts.outfit(
fontSize: 12,
color: AppColors.warmGray,
=======
State<CycleRing> createState() => _CycleRingState();
}
class _CycleRingState extends State<CycleRing> with SingleTickerProviderStateMixin {
class _CycleRingState extends State<CycleRing>
with SingleTickerProviderStateMixin {
late AnimationController _controller;
late Animation<double> _animation;
@@ -125,7 +55,7 @@ class _CycleRingState extends State<CycleRing> with SingleTickerProviderStateMix
animation: _animation,
builder: (context, child) {
final currentProgress = targetProgress * _animation.value;
return SizedBox(
width: 220,
height: 220,
@@ -141,7 +71,7 @@ class _CycleRingState extends State<CycleRing> with SingleTickerProviderStateMix
isDark: isDark,
),
),
// Center content with scale and fade animation
Transform.scale(
scale: 0.8 + (0.2 * _animation.value),
@@ -152,19 +82,28 @@ class _CycleRingState extends State<CycleRing> with SingleTickerProviderStateMix
children: [
Text(
'Day ${widget.dayOfCycle}',
style: Theme.of(context).textTheme.displayMedium?.copyWith(
fontSize: 32,
fontWeight: FontWeight.w600,
color: Theme.of(context).colorScheme.onSurface,
),
style: Theme.of(context)
.textTheme
.displayMedium
?.copyWith(
fontSize: 32,
fontWeight: FontWeight.w600,
color: Theme.of(context).colorScheme.onSurface,
),
),
const SizedBox(height: 4),
Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
padding: const EdgeInsets.symmetric(
horizontal: 12, vertical: 6),
decoration: BoxDecoration(
color: _getPhaseColor(widget.phase).withOpacity(isDark ? 0.3 : 0.2),
color: _getPhaseColor(widget.phase)
.withOpacity(isDark ? 0.3 : 0.2),
borderRadius: BorderRadius.circular(20),
border: isDark ? Border.all(color: _getPhaseColor(widget.phase).withOpacity(0.5)) : null,
border: isDark
? Border.all(
color: _getPhaseColor(widget.phase)
.withOpacity(0.5))
: null,
),
child: Row(
mainAxisSize: MainAxisSize.min,
@@ -179,7 +118,9 @@ class _CycleRingState extends State<CycleRing> with SingleTickerProviderStateMix
style: GoogleFonts.outfit(
fontSize: 14,
fontWeight: FontWeight.w500,
color: isDark ? Colors.white : _getPhaseColor(widget.phase),
color: isDark
? Colors.white
: _getPhaseColor(widget.phase),
),
),
],
@@ -191,24 +132,19 @@ class _CycleRingState extends State<CycleRing> with SingleTickerProviderStateMix
? '$daysUntilNextPeriod days until period'
: 'Period expected',
style: Theme.of(context).textTheme.bodySmall?.copyWith(
fontSize: 12,
color: Theme.of(context).colorScheme.onSurfaceVariant,
),
fontSize: 12,
color:
Theme.of(context).colorScheme.onSurfaceVariant,
),
),
],
),
>>>>>>> 6742220 (Your commit message here)
),
),
],
),
<<<<<<< HEAD
],
),
=======
);
},
>>>>>>> 6742220 (Your commit message here)
);
}
@@ -229,18 +165,13 @@ class _CycleRingState extends State<CycleRing> with SingleTickerProviderStateMix
class _CycleRingPainter extends CustomPainter {
final double progress;
final CyclePhase phase;
<<<<<<< HEAD
_CycleRingPainter({required this.progress, required this.phase});
=======
final bool isDark;
_CycleRingPainter({
required this.progress,
required this.progress,
required this.phase,
required this.isDark,
});
>>>>>>> 6742220 (Your commit message here)
@override
void paint(Canvas canvas, Size size) {
@@ -250,11 +181,8 @@ class _CycleRingPainter extends CustomPainter {
// Background arc
final bgPaint = Paint()
<<<<<<< HEAD
..color = AppColors.lightGray.withOpacity(0.2)
=======
..color = (isDark ? Colors.white : AppColors.lightGray).withOpacity(isDark ? 0.05 : 0.1)
>>>>>>> 6742220 (Your commit message here)
..color =
(isDark ? Colors.white : AppColors.lightGray).withOpacity(isDark ? 0.05 : 0.1)
..style = PaintingStyle.stroke
..strokeWidth = strokeWidth
..strokeCap = StrokeCap.round;
@@ -287,11 +215,7 @@ class _CycleRingPainter extends CustomPainter {
final dotY = center.dy + radius * math.sin(dotAngle);
final dotPaint = Paint()
<<<<<<< HEAD
..color = Colors.white
=======
..color = isDark ? const Color(0xFF1E1E1E) : Colors.white
>>>>>>> 6742220 (Your commit message here)
..style = PaintingStyle.fill;
final dotBorderPaint = Paint()
@@ -308,11 +232,19 @@ class _CycleRingPainter extends CustomPainter {
case CyclePhase.menstrual:
return [AppColors.rose, AppColors.menstrualPhase, AppColors.blushPink];
case CyclePhase.follicular:
return [AppColors.sageGreen, AppColors.follicularPhase, AppColors.sageGreen.withOpacity(0.7)];
return [
AppColors.sageGreen,
AppColors.follicularPhase,
AppColors.sageGreen.withOpacity(0.7)
];
case CyclePhase.ovulation:
return [AppColors.lavender, AppColors.ovulationPhase, AppColors.rose];
case CyclePhase.luteal:
return [AppColors.lutealPhase, AppColors.lavender, AppColors.blushPink];
return [
AppColors.lutealPhase,
AppColors.lavender,
AppColors.blushPink
];
}
}

View File

@@ -1,43 +1,4 @@
import 'package:flutter/material.dart';
<<<<<<< HEAD
import 'package:google_fonts/google_fonts.dart';
import '../theme/app_theme.dart';
import '../screens/log/log_screen.dart';
class QuickLogButtons extends StatelessWidget {
const QuickLogButtons({super.key});
@override
Widget build(BuildContext context) {
return Row(
children: [
_buildQuickButton(
icon: Icons.water_drop_outlined,
label: 'Period',
color: AppColors.menstrualPhase,
onTap: () => _navigateToLog(context),
),
const SizedBox(width: 12),
_buildQuickButton(
icon: Icons.emoji_emotions_outlined,
label: 'Mood',
color: AppColors.softGold,
onTap: () => _navigateToLog(context),
),
const SizedBox(width: 12),
_buildQuickButton(
icon: Icons.flash_on_outlined,
label: 'Energy',
color: AppColors.follicularPhase,
onTap: () => _navigateToLog(context),
),
const SizedBox(width: 12),
_buildQuickButton(
icon: Icons.healing_outlined,
label: 'Symptoms',
color: AppColors.lavender,
onTap: () => _navigateToLog(context),
=======
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:google_fonts/google_fonts.dart';
import '../theme/app_theme.dart';
@@ -80,32 +41,11 @@ class QuickLogButtons extends ConsumerWidget {
label: 'Symptoms',
color: AppColors.lavender,
onTap: () => _navigateToLog(ref),
>>>>>>> 6742220 (Your commit message here)
),
],
);
}
<<<<<<< HEAD
void _navigateToLog(BuildContext context) {
// Navigate to the Log tab (index 2) of HomeScreen if possible,
// but since we are inside a tab, we can't easily switch the parent tab index without context. Using a provider or callback would be best.
// For now, let's push the LogScreen as a new route for "Quick Log" feel.
// Ideally we would switch the BottomNavBar index.
Navigator.of(context).push(
MaterialPageRoute(builder: (context) => const Scaffold(
appBar: PreferredSize(
preferredSize: Size.fromHeight(0),
child: SizedBox.shrink()
),
body: LogScreen()
)),
);
}
Widget _buildQuickButton({
=======
void _navigateToLog(WidgetRef ref) {
// Navigate to the Log tab (index 2)
ref.read(navigationProvider.notifier).setIndex(2);
@@ -113,39 +53,13 @@ class QuickLogButtons extends ConsumerWidget {
Widget _buildQuickButton(
BuildContext context, {
>>>>>>> 6742220 (Your commit message here)
required IconData icon,
required String label,
required Color color,
required VoidCallback onTap,
}) {
<<<<<<< HEAD
return Expanded(
child: GestureDetector(
onTap: onTap,
child: Container(
padding: const EdgeInsets.symmetric(vertical: 16),
decoration: BoxDecoration(
color: color.withOpacity(0.15),
borderRadius: BorderRadius.circular(12),
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Icon(icon, color: color, size: 24),
const SizedBox(height: 6),
Text(
label,
style: GoogleFonts.outfit(
fontSize: 11,
fontWeight: FontWeight.w500,
color: color,
),
),
],
=======
final isDark = Theme.of(context).brightness == Brightness.dark;
return Expanded(
child: Material(
color: Colors.transparent,
@@ -157,7 +71,8 @@ class QuickLogButtons extends ConsumerWidget {
decoration: BoxDecoration(
color: color.withOpacity(isDark ? 0.2 : 0.15),
borderRadius: BorderRadius.circular(12),
border: isDark ? Border.all(color: color.withOpacity(0.3)) : null,
border:
isDark ? Border.all(color: color.withOpacity(0.3)) : null,
),
child: Column(
mainAxisSize: MainAxisSize.min,
@@ -174,7 +89,6 @@ class QuickLogButtons extends ConsumerWidget {
),
],
),
>>>>>>> 6742220 (Your commit message here)
),
),
),

View File

@@ -6,122 +6,45 @@ import '../models/cycle_entry.dart';
class ScriptureCard extends StatelessWidget {
final String verse;
final String reference;
<<<<<<< HEAD
final CyclePhase phase;
=======
final String? translation;
final CyclePhase phase;
final VoidCallback? onTranslationTap;
>>>>>>> 6742220 (Your commit message here)
const ScriptureCard({
super.key,
required this.verse,
required this.reference,
<<<<<<< HEAD
required this.phase,
=======
this.translation,
required this.phase,
this.onTranslationTap,
>>>>>>> 6742220 (Your commit message here)
});
@override
Widget build(BuildContext context) {
<<<<<<< HEAD
return Container(
width: double.infinity,
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
gradient: LinearGradient(
colors: _getGradientColors(phase),
=======
final theme = Theme.of(context);
final isDark = theme.brightness == Brightness.dark;
return Container(
width: double.infinity,
decoration: BoxDecoration(
gradient: LinearGradient(
colors: _getGradientColors(context, phase),
>>>>>>> 6742220 (Your commit message here)
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
borderRadius: BorderRadius.circular(20),
<<<<<<< HEAD
boxShadow: [
BoxShadow(
color: _getPhaseColor(phase).withOpacity(0.2),
=======
border: Border.all(color: isDark ? Colors.white.withOpacity(0.05) : Colors.black.withOpacity(0.05)),
border: Border.all(
color: isDark
? Colors.white.withOpacity(0.05)
: Colors.black.withOpacity(0.05)),
boxShadow: [
BoxShadow(
color: _getPhaseColor(phase).withOpacity(isDark ? 0.05 : 0.15),
>>>>>>> 6742220 (Your commit message here)
blurRadius: 15,
offset: const Offset(0, 8),
),
],
),
<<<<<<< HEAD
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Scripture icon
Row(
children: [
Container(
width: 32,
height: 32,
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.3),
borderRadius: BorderRadius.circular(8),
),
child: Icon(
Icons.menu_book_outlined,
size: 18,
color: AppColors.charcoal.withOpacity(0.8),
),
),
const SizedBox(width: 8),
Text(
'Today\'s Verse',
style: GoogleFonts.outfit(
fontSize: 12,
fontWeight: FontWeight.w500,
color: AppColors.charcoal.withOpacity(0.7),
letterSpacing: 0.5,
),
),
],
),
const SizedBox(height: 16),
// Verse
Text(
'"$verse"',
style: GoogleFonts.lora(
fontSize: 16,
fontStyle: FontStyle.italic,
color: AppColors.charcoal,
height: 1.6,
),
),
const SizedBox(height: 12),
// Reference
Text(
'$reference',
style: GoogleFonts.outfit(
fontSize: 13,
fontWeight: FontWeight.w500,
color: AppColors.warmGray,
),
),
],
=======
child: Material(
color: Colors.transparent,
child: Padding(
@@ -136,13 +59,16 @@ class ScriptureCard extends StatelessWidget {
width: 32,
height: 32,
decoration: BoxDecoration(
color: (isDark ? Colors.white : Colors.black).withOpacity(0.1),
color: (isDark ? Colors.white : Colors.black)
.withOpacity(0.1),
borderRadius: BorderRadius.circular(8),
),
child: Icon(
Icons.menu_book_outlined,
size: 18,
color: isDark ? Colors.white70 : AppColors.charcoal.withOpacity(0.8),
color: isDark
? Colors.white70
: AppColors.charcoal.withOpacity(0.8),
),
),
const SizedBox(width: 8),
@@ -150,40 +76,46 @@ class ScriptureCard extends StatelessWidget {
'Today\'s Verse',
style: theme.textTheme.labelLarge?.copyWith(
fontSize: 12,
color: isDark ? Colors.white60 : AppColors.charcoal.withOpacity(0.7),
color: isDark
? Colors.white60
: AppColors.charcoal.withOpacity(0.7),
letterSpacing: 0.5,
),
),
],
),
const SizedBox(height: 16),
// Verse
Text(
'"$verse"',
style: scriptureStyle(context, fontSize: 17),
),
const SizedBox(height: 12),
// Reference & Translation
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'$reference',
style: scriptureRefStyle(context).copyWith(fontSize: 13, fontWeight: FontWeight.w600),
style: scriptureRefStyle(context)
.copyWith(fontSize: 13, fontWeight: FontWeight.w600),
),
if (translation != null)
InkWell(
onTap: onTranslationTap,
borderRadius: BorderRadius.circular(8),
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 6),
padding: const EdgeInsets.symmetric(
horizontal: 10, vertical: 6),
decoration: BoxDecoration(
color: (isDark ? Colors.white : Colors.black).withOpacity(0.05),
color: (isDark ? Colors.white : Colors.black)
.withOpacity(0.05),
borderRadius: BorderRadius.circular(8),
border: Border.all(
color: (isDark ? Colors.white : Colors.black).withOpacity(0.1),
color: (isDark ? Colors.white : Colors.black)
.withOpacity(0.1),
),
),
child: Row(
@@ -199,9 +131,11 @@ class ScriptureCard extends StatelessWidget {
),
const SizedBox(width: 4),
Icon(
Icons.swap_horiz,
size: 14,
color: isDark ? Colors.white38 : AppColors.warmGray,
Icons.swap_horiz,
size: 14,
color: isDark
? Colors.white38
: AppColors.warmGray,
),
],
),
@@ -212,38 +146,14 @@ class ScriptureCard extends StatelessWidget {
],
),
),
>>>>>>> 6742220 (Your commit message here)
),
);
}
<<<<<<< HEAD
List<Color> _getGradientColors(CyclePhase phase) {
switch (phase) {
case CyclePhase.menstrual:
return [
AppColors.blushPink.withOpacity(0.6),
AppColors.cream,
];
case CyclePhase.follicular:
return [
AppColors.sageGreen.withOpacity(0.3),
AppColors.cream,
];
case CyclePhase.ovulation:
return [
AppColors.lavender.withOpacity(0.5),
AppColors.cream,
];
case CyclePhase.luteal:
return [
AppColors.lutealPhase.withOpacity(0.3),
AppColors.cream,
=======
List<Color> _getGradientColors(BuildContext context, CyclePhase phase) {
final isDark = Theme.of(context).brightness == Brightness.dark;
final baseColor = isDark ? const Color(0xFF1E1E1E) : AppColors.cream;
switch (phase) {
case CyclePhase.menstrual:
return [
@@ -264,7 +174,6 @@ class ScriptureCard extends StatelessWidget {
return [
AppColors.lutealPhase.withOpacity(isDark ? 0.15 : 0.3),
baseColor,
>>>>>>> 6742220 (Your commit message here)
];
}
}

View File

@@ -15,31 +15,22 @@ class TipCard extends StatelessWidget {
@override
Widget build(BuildContext context) {
<<<<<<< HEAD
=======
final theme = Theme.of(context);
final isDark = theme.brightness == Brightness.dark;
>>>>>>> 6742220 (Your commit message here)
final tip = _getTipForPhase(phase, isMarried);
return Container(
width: double.infinity,
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
<<<<<<< HEAD
color: Colors.white,
borderRadius: BorderRadius.circular(16),
boxShadow: [
BoxShadow(
color: AppColors.charcoal.withOpacity(0.05),
=======
color: theme.cardColor,
borderRadius: BorderRadius.circular(16),
border: isDark ? Border.all(color: Colors.white.withOpacity(0.05)) : null,
border:
isDark ? Border.all(color: Colors.white.withOpacity(0.05)) : null,
boxShadow: [
BoxShadow(
color: (isDark ? Colors.black : AppColors.charcoal).withOpacity(0.05),
>>>>>>> 6742220 (Your commit message here)
color: (isDark ? Colors.black : AppColors.charcoal)
.withOpacity(0.05),
blurRadius: 10,
offset: const Offset(0, 4),
),
@@ -52,11 +43,7 @@ class TipCard extends StatelessWidget {
width: 40,
height: 40,
decoration: BoxDecoration(
<<<<<<< HEAD
color: AppColors.sageGreen.withOpacity(0.15),
=======
color: AppColors.sageGreen.withOpacity(isDark ? 0.2 : 0.15),
>>>>>>> 6742220 (Your commit message here)
borderRadius: BorderRadius.circular(10),
),
child: const Icon(
@@ -72,29 +59,14 @@ class TipCard extends StatelessWidget {
children: [
Text(
'Today\'s Tip',
<<<<<<< HEAD
style: GoogleFonts.outfit(
fontSize: 14,
fontWeight: FontWeight.w600,
color: AppColors.charcoal,
=======
style: theme.textTheme.titleMedium?.copyWith(
fontSize: 14,
fontWeight: FontWeight.w600,
>>>>>>> 6742220 (Your commit message here)
),
),
const SizedBox(height: 4),
Text(
tip,
<<<<<<< HEAD
style: GoogleFonts.outfit(
fontSize: 13,
color: AppColors.warmGray,
height: 1.4,
),
),
=======
style: theme.textTheme.bodyMedium?.copyWith(
fontSize: 13,
height: 1.4,
@@ -122,7 +94,6 @@ class TipCard extends StatelessWidget {
],
),
),
>>>>>>> 6742220 (Your commit message here)
],
),
),
@@ -131,21 +102,6 @@ class TipCard extends StatelessWidget {
);
}
<<<<<<< HEAD
String _getTipForPhase(CyclePhase phase, bool isMarried) {
switch (phase) {
case CyclePhase.menstrual:
return 'This is a time for rest. Honor your body with extra sleep, warm drinks, and gentle movement. God designed your body with wisdom.';
case CyclePhase.follicular:
return 'Your energy is rising! This is a great time to start new projects, exercise more intensely, and spend time in community.';
case CyclePhase.ovulation:
if (isMarried) {
return 'This is your most fertile window. You may feel more social and energetic. Prioritize connection with your spouse.';
}
return 'You may feel more social and confident during this phase. It\'s a great time for important conversations and presentations.';
case CyclePhase.luteal:
return 'As you enter the luteal phase, focus on nourishing foods, adequate sleep, and stress management. Be gentle with yourself.';
=======
void _showDetailedInsights(BuildContext context) {
final details = _getDetailsForPhase(phase);
showDialog(
@@ -170,9 +126,11 @@ class TipCard extends StatelessWidget {
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildDetailSection('Nutrition', details['nutrition']!, Icons.restaurant),
_buildDetailSection(
'Nutrition', details['nutrition']!, Icons.restaurant),
const SizedBox(height: 16),
_buildDetailSection('Movement', details['movement']!, Icons.fitness_center),
_buildDetailSection(
'Movement', details['movement']!, Icons.fitness_center),
const SizedBox(height: 16),
Text(
'Note: While these are general trends, your body is unique. Always listen to your own energy levels.',
@@ -187,7 +145,8 @@ class TipCard extends StatelessWidget {
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: Text('Got it', style: GoogleFonts.outfit(color: AppColors.sageGreen)),
child: Text('Got it',
style: GoogleFonts.outfit(color: AppColors.sageGreen)),
),
],
),
@@ -243,25 +202,32 @@ class TipCard extends StatelessWidget {
switch (phase) {
case CyclePhase.menstrual:
return {
'nutrition': 'Incorporate leafy greens, red meat or lentils for iron. Pair with citrus for better absorption.',
'movement': 'Gentle walking, restorative yoga, or just deep breathing. Avoid high-intensity stress.',
'nutrition':
'Incorporate leafy greens, red meat or lentils for iron. Pair with citrus for better absorption.',
'movement':
'Gentle walking, restorative yoga, or just deep breathing. Avoid high-intensity stress.',
};
case CyclePhase.follicular:
return {
'nutrition': 'Broccoli, cauliflower, and fermented foods help balance rising estrogen. Focus on lean proteins.',
'movement': 'Strength training and steady cardio. Your body is primed for building and renewal.',
'nutrition':
'Broccoli, cauliflower, and fermented foods help balance rising estrogen. Focus on lean proteins.',
'movement':
'Strength training and steady cardio. Your body is primed for building and renewal.',
};
case CyclePhase.ovulation:
return {
'nutrition': 'Avocados, nuts, and seeds provide healthy fats for peak hormonal health. Keep hydration high.',
'movement': 'Highest intensity workouts, HIIT, or group sports. You have peak stamina and strength right now.',
'nutrition':
'Avocados, nuts, and seeds provide healthy fats for peak hormonal health. Keep hydration high.',
'movement':
'Highest intensity workouts, HIIT, or group sports. You have peak stamina and strength right now.',
};
case CyclePhase.luteal:
return {
'nutrition': 'Dark chocolate (70%+), pumpkin seeds, and bananas for magnesium to help with cramps.',
'movement': 'Pilates, steady-state swimming, or hiking. Focus on persistence rather than peak intensity.',
'nutrition':
'Dark chocolate (70%+), pumpkin seeds, and bananas for magnesium to help with cramps.',
'movement':
'Pilates, steady-state swimming, or hiking. Focus on persistence rather than peak intensity.',
};
>>>>>>> 6742220 (Your commit message here)
}
}
}