USER
Refactor this code. This is for an expo mobile app. Make sure its dynamic.
// RoutineCreationScreen.js
import React, { useState, useContext } from 'react';
import { View, ScrollView, Alert } from 'react-native';
import {
Text,
Button,
ProgressBar,
Chip,
useTheme,
TextInput,
Card,
} from 'react-native-paper';
import { AuthContext } from '../../context/AuthContext';
import Icon from 'react-native-vector-icons/MaterialCommunityIcons';
import RNPickerSelect from 'react-native-picker-select';
import getStyles from './RoutineCreationStyles';
const STEPS = {
FITNESS_GOALS: 0,
SCHEDULE: 1,
EXERCISES: 2,
REST_RECOVERY: 3,
REVIEW: 4,
};
const RoutineCreationScreen = ({ navigation }) => {
const theme = useTheme();
const styles = getStyles(theme);
const { user } = useContext(AuthContext);
const [currentStep, setCurrentStep] = useState(STEPS.FITNESS_GOALS);
const [routineData, setRoutineData] = useState({
goal: '',
customGoal: '',
schedule: {
frequency: '',
duration: '',
preferredDays: [],
timeOfDay: '',
intensity: '',
},
exercises: [],
rest: {
restDays: [],
cooldownRoutine: [],
restBetweenSets: '',
},
routineName: '',
});
const fitnessGoals = [
{ label: 'Build Muscle', value: 'muscle', icon: 'dumbbell' },
{ label: 'Lose Weight', value: 'weight', icon: 'scale' },
{ label: 'Increase Flexibility', value: 'flexibility', icon: 'yoga' },
{ label: 'Improve Endurance', value: 'endurance', icon: 'run' },
{ label: 'Toning', value: 'toning', icon: 'human' },
{ label: 'General Fitness', value: 'general', icon: 'heart-pulse' },
{ label: 'Custom Goal', value: 'custom', icon: 'pencil' },
];
const renderFitnessGoalsStep = () => (
<View>
<Text style={styles.stepTitle}>Select Your Fitness Goals</Text>
<View style={styles.goalsContainer}>
{fitnessGoals.map((goal) => (
<Chip
key={goal.value}
onPress={() => updateRoutineData('goal', goal.value)}
selected={routineData.goal === goal.value}
style={[styles.goalChip, routineData.goal === goal.value && { backgroundColor: theme.colors.primary }]}
selectedColor={theme.colors.primary}
textStyle={{ color: routineData.goal === goal.value ? '#fff' : theme.colors.primary }}
icon={() => (
<Icon
name={goal.icon}
size={20}
color={routineData.goal === goal.value ? '#fff' : theme.colors.primary}
/>
)}
>
{goal.label}
</Chip>
))}
</View>
{routineData.goal === 'custom' && (
<TextInput
label="Custom Goal"
value={routineData.customGoal}
onChangeText={(text) => updateRoutineData('customGoal', text)}
style={styles.input}
mode="outlined"
placeholder="Enter your custom goal"
theme={{ colors: { primary: theme.colors.primary, background: theme.colors.background } }}
/>
)}
</View>
);
const renderScheduleStep = () => (
<View>
<Text style={styles.stepTitle}>Choose Your Workout Schedule</Text>
<Text style={styles.inputLabel}>Workout Frequency</Text>
<RNPickerSelect
onValueChange={(value) => updateSchedule('frequency', value)}
items={[
{ label: '1 day per week', value: '1' },
{ label: '2 days per week', value: '2' },
{ label: '3 days per week', value: '3' },
{ label: '4 days per week', value: '4' },
{ label: '5 days per week', value: '5' },
{ label: '6 days per week', value: '6' },
{ label: '7 days per week', value: '7' },
]}
placeholder={{ label: 'Select workout frequency', value: null }}
value={routineData.schedule.frequency}
style={styles.picker}
useNativeAndroidPickerStyle={false}
Icon={() => <Icon name="chevron-down" size={20} color={theme.colors.text} />}
/>
<Text style={styles.inputLabel}>Workout Duration</Text>
<RNPickerSelect
onValueChange={(value) => updateSchedule('duration', value)}
items={[
{ label: '30 minutes', value: '30' },
{ label: '45 minutes', value: '45' },
{ label: '60 minutes', value: '60' },
{ label: '90 minutes', value: '90' },
]}
placeholder={{ label: 'Select workout duration', value: null }}
value={routineData.schedule.duration}
style={styles.picker}
useNativeAndroidPickerStyle={false}
Icon={() => <Icon name="chevron-down" size={20} color={theme.colors.text} />}
/>
<Text style={styles.inputLabel}>Preferred Days</Text>
<View style={styles.daysContainer}>
{['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'].map((day) => (
<Chip
key={day}
onPress={() => togglePreferredDay(day)}
selected={routineData.schedule.preferredDays.includes(day)}
style={[styles.dayChip, routineData.schedule.preferredDays.includes(day) && { backgroundColor: theme.colors.primary }]}
selectedColor={theme.colors.primary}
textStyle={{ color: routineData.schedule.preferredDays.includes(day) ? '#fff' : theme.colors.primary }}
>
{day}
</Chip>
))}
</View>
<Text style={styles.inputLabel}>Time of Day</Text>
<RNPickerSelect
onValueChange={(value) => updateSchedule('timeOfDay', value)}
items={[
{ label: 'Morning', value: 'morning' },
{ label: 'Afternoon', value: 'afternoon' },
{ label: 'Evening', value: 'evening' },
]}
placeholder={{ label: 'Select time of day', value: null }}
value={routineData.schedule.timeOfDay}
style={styles.picker}
useNativeAndroidPickerStyle={false}
Icon={() => <Icon name="chevron-down" size={20} color={theme.colors.text} />}
/>
<Text style={styles.inputLabel}>Intensity</Text>
<RNPickerSelect
onValueChange={(value) => updateSchedule('intensity', value)}
items={[
{ label: 'Low', value: 'low' },
{ label: 'Medium', value: 'medium' },
{ label: 'High', value: 'high' },
]}
placeholder={{ label: 'Select intensity level', value: null }}
value={routineData.schedule.intensity}
style={styles.picker}
useNativeAndroidPickerStyle={false}
Icon={() => <Icon name="chevron-down" size={20} color={theme.colors.text} />}
/>
</View>
);
const renderExercisesStep = () => (
<View>
<Text style={styles.stepTitle}>Select Your Exercises</Text>
{/* Placeholder for exercise selection UI */}
<Text style={{ color: theme.colors.text }}>
Implement exercise selection here. You can include search functionality,
categories, and selection of multiple exercises.
</Text>
</View>
);
const renderRestRecoveryStep = () => (
<View>
<Text style={styles.stepTitle}>Configure Rest & Recovery</Text>
<Text style={styles.inputLabel}>Rest Days</Text>
<View style={styles.daysContainer}>
{['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'].map((day) => (
<Chip
key={day}
onPress={() => toggleRestDay(day)}
selected={routineData.rest.restDays.includes(day)}
style={[styles.dayChip, routineData.rest.restDays.includes(day) && { backgroundColor: theme.colors.primary }]}
selectedColor={theme.colors.primary}
textStyle={{ color: routineData.rest.restDays.includes(day) ? '#fff' : theme.colors.primary }}
>
{day}
</Chip>
))}
</View>
<Text style={styles.inputLabel}>Cooldown Routine</Text>
{/* Placeholder for cooldown routine selection */}
<Text style={{ color: theme.colors.text }}>
Implement cooldown routine selection here. You can include predefined cooldowns or allow users to create their own.
</Text>
<Text style={styles.inputLabel}>Rest Between Sets (seconds)</Text>
<TextInput
label="Rest Between Sets"
value={routineData.rest.restBetweenSets}
onChangeText={(text) => updateRest('restBetweenSets', text)}
style={styles.input}
mode="outlined"
keyboardType="numeric"
placeholder="e.g., 30"
theme={{ colors: { primary: theme.colors.primary, background: theme.colors.background } }}
/>
</View>
);
const renderReviewStep = () => (
<View>
<Text style={styles.stepTitle}>Review Your Routine</Text>
<Card style={styles.reviewCard}>
<Card.Title title="Routine Name" />
<Card.Content>
<Text>{routineData.routineName || 'N/A'}</Text>
</Card.Content>
</Card>
<Card style={styles.reviewCard}>
<Card.Title title="Fitness Goal" />
<Card.Content>
<Text>
{routineData.goal === 'custom'
? routineData.customGoal
: fitnessGoals.find((goal) => goal.value === routineData.goal)?.label || 'N/A'}
</Text>
</Card.Content>
</Card>
<Card style={styles.reviewCard}>
<Card.Title title="Schedule" />
<Card.Content>
<Text>Frequency: {routineData.schedule.frequency} days/week</Text>
<Text>Duration: {routineData.schedule.duration} minutes</Text>
<Text>Preferred Days: {routineData.schedule.preferredDays.join(', ') || 'N/A'}</Text>
<Text>Time of Day: {capitalizeFirstLetter(routineData.schedule.timeOfDay) || 'N/A'}</Text>
<Text>Intensity: {capitalizeFirstLetter(routineData.schedule.intensity) || 'N/A'}</Text>
</Card.Content>
</Card>
{/* Add Cards for Exercises and Rest & Recovery as they are implemented */}
</View>
);
// Helper Function to Capitalize
const capitalizeFirstLetter = (string) => {
if (!string) return '';
return string.charAt(0).toUpperCase() + string.slice(1);
};
// Update routine data
const updateRoutineData = (field, value) => {
setRoutineData((prev) => ({
...prev,
[field]: value,
}));
};
// Update schedule data
const updateSchedule = (field, value) => {
setRoutineData((prev) => ({
...prev,
schedule: {
...prev.schedule,
[field]: value,
},
}));
};
// Update rest data
const updateRest = (field, value) => {
setRoutineData((prev) => ({
...prev,
rest: {
...prev.rest,
[field]: value,
},
}));
};
// Toggle preferred workout days
const togglePreferredDay = (day) => {
setRoutineData((prev) => {
const currentDays = prev.schedule.preferredDays;
const updatedDays = currentDays.includes(day)
? currentDays.filter((d) => d !== day)
: [...currentDays, day];
return {
...prev,
schedule: {
...prev.schedule,
preferredDays: updatedDays,
},
};
});
};
// Toggle rest days
const toggleRestDay = (day) => {
setRoutineData((prev) => {
const currentDays = prev.rest.restDays;
const updatedDays = currentDays.includes(day)
? currentDays.filter((d) => d !== day)
: [...currentDays, day];
return {
...prev,
rest: {
...prev.rest,
restDays: updatedDays,
},
};
});
};
// Handle Next Button Press
const handleNext = () => {
if (!validateCurrentStep()) return;
if (currentStep < STEPS.REVIEW) {
setCurrentStep(currentStep + 1);
} else {
// Handle routine creation logic here
Alert.alert('Success', 'Routine created successfully!');
navigation.goBack();
}
};
// Handle Back Button Press
const handleBack = () => {
if (currentStep > STEPS.FITNESS_GOALS) {
setCurrentStep(currentStep - 1);
} else {
navigation.goBack();
}
};
// Validate Current Step
const validateCurrentStep = () => {
switch (currentStep) {
case STEPS.FITNESS_GOALS:
if (!routineData.goal) {
Alert.alert('Validation Error', 'Please select a fitness goal.');
return false;
}
if (routineData.goal === 'custom' && !routineData.customGoal.trim()) {
Alert.alert('Validation Error', 'Please enter your custom goal.');
return false;
}
return true;
case STEPS.SCHEDULE:
const { frequency, duration, preferredDays, timeOfDay, intensity } = routineData.schedule;
if (!frequency || !duration || preferredDays.length === 0 || !timeOfDay || !intensity) {
Alert.alert('Validation Error', 'Please complete all schedule fields.');
return false;
}
return true;
case STEPS.EXERCISES:
if (routineData.exercises.length === 0) {
Alert.alert('Validation Error', 'Please select at least one exercise.');
return false;
}
return true;
case STEPS.REST_RECOVERY:
const { restDays, restBetweenSets } = routineData.rest;
if (restDays.length === 0 || !restBetweenSets) {
Alert.alert('Validation Error', 'Please complete all rest & recovery fields.');
return false;
}
return true;
case STEPS.REVIEW:
if (!routineData.routineName.trim()) {
Alert.alert('Validation Error', 'Please provide a name for your routine.');
return false;
}
return true;
default:
return true;
}
};
const renderStepContent = () => {
switch (currentStep) {
case STEPS.FITNESS_GOALS:
return renderFitnessGoalsStep();
case STEPS.SCHEDULE:
return renderScheduleStep();
case STEPS.EXERCISES:
return renderExercisesStep();
case STEPS.REST_RECOVERY:
return renderRestRecoveryStep();
case STEPS.REVIEW:
return renderReviewStep();
default:
return null;
}
};
return (
<ScrollView style={styles.container} contentContainerStyle={styles.contentContainer}>
<ProgressBar
progress={(currentStep + 1) / Object.keys(STEPS).length}
color={theme.colors.primary}
style={styles.progressBar}
/>
{renderStepContent()}
<View style={styles.navigationContainer}>
<Button
mode="outlined"
onPress={handleBack}
style={styles.navButton}
color={theme.colors.primary}
>
{currentStep === STEPS.FITNESS_GOALS ? 'Cancel' : 'Back'}
</Button>
<Button
mode="contained"
onPress={handleNext}
style={styles.navButton}
color={theme.colors.primary}
>
{currentStep === STEPS.REVIEW ? 'Create Routine' : 'Next'}
</Button>
</View>
</ScrollView>
);
};
export default RoutineCreationScreen;ASSISTANT
Certainly! Refactoring your `RoutineCreationScreen` to be more dynamic and modular can greatly enhance readability, maintainability, and scalability. Below is a refactored version of your code that accomplishes the following:
1. **Modular Components:** Each step is extracted into its own component, making the code cleaner and easier to manage.
2. **Dynamic Step Configuration:** Steps are defined in a configuration array, allowing for easy addition or modification of steps.
3. **Reusable UI Elements:** Common UI elements like `DaySelector` and `CustomPicker` are created to avoid repetition.
4. **Enhanced State Management:** Utilizes more granular state update functions for better clarity.
5. **Validation Logic:** Each step handles its own validation, making the validation process more organized.
### Refactored Code
#### 1. Create Reusable Components
First, let's create some reusable components that can be utilized across different steps.
**`CustomPicker.js`**
```javascript
// CustomPicker.js
import React from 'react';
import RNPickerSelect from 'react-native-picker-select';
import Icon from 'react-native-vector-icons/MaterialCommunityIcons';
import { useTheme } from 'react-native-paper';
const CustomPicker = ({ label, value, onValueChange, items, placeholder }) => {
const theme = useTheme();
return (
<>
<Text style={{ marginTop: 16 }}>{label}</Text>
<RNPickerSelect
onValueChange={onValueChange}
items={items}
placeholder={{ label: placeholder, value: null }}
value={value}
style={{
...pickerStyles,
iconContainer: {
top: 15,
right: 10,
},
}}
useNativeAndroidPickerStyle={false}
Icon={() => <Icon name="chevron-down" size={20} color={theme.colors.text} />}
/>
</>
);
};
const pickerStyles = {
inputIOS: {
fontSize: 16,
paddingVertical: 12,
paddingHorizontal: 10,
borderWidth: 1,
borderColor: '#ccc',
borderRadius: 4,
color: 'black',
paddingRight: 30, // to ensure the text is never behind the icon
marginTop: 8,
},
inputAndroid: {
fontSize: 16,
paddingHorizontal: 10,
paddingVertical: 8,
borderWidth: 0.5,
borderColor: '#ccc',
borderRadius: 8,
color: 'black',
paddingRight: 30, // to ensure the text is never behind the icon
marginTop: 8,
},
};
export default CustomPicker;
```
**`DaySelector.js`**
```javascript
// DaySelector.js
import React from 'react';
import { View } from 'react-native';
import { Chip, useTheme } from 'react-native-paper';
const DaySelector = ({ selectedDays, toggleDay, days = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'] }) => {
const theme = useTheme();
return (
<View style={{ flexDirection: 'row', flexWrap: 'wrap', marginTop: 8 }}>
{days.map((day) => (
<Chip
key={day}
onPress={() => toggleDay(day)}
selected={selectedDays.includes(day)}
style={[
{ margin: 4 },
selectedDays.includes(day) && { backgroundColor: theme.colors.primary },
]}
selectedColor="#fff"
textStyle={{ color: selectedDays.includes(day) ? '#fff' : theme.colors.primary }}
>
{day}
</Chip>
))}
</View>
);
};
export default DaySelector;
```
#### 2. Define Step Components
Next, define each step as its own component. This makes each step self-contained and easier to manage.
**`FitnessGoalsStep.js`**
```javascript
// FitnessGoalsStep.js
import React from 'react';
import { View } from 'react-native';
import { Text, Chip, TextInput, useTheme } from 'react-native-paper';
import Icon from 'react-native-vector-icons/MaterialCommunityIcons';
import DaySelector from './DaySelector';
const FitnessGoalsStep = ({ routineData, updateRoutineData }) => {
const theme = useTheme();
const fitnessGoals = [
{ label: 'Build Muscle', value: 'muscle', icon: 'dumbbell' },
{ label: 'Lose Weight', value: 'weight', icon: 'scale' },
{ label: 'Increase Flexibility', value: 'flexibility', icon: 'yoga' },
{ label: 'Improve Endurance', value: 'endurance', icon: 'run' },
{ label: 'Toning', value: 'toning', icon: 'human' },
{ label: 'General Fitness', value: 'general', icon: 'heart-pulse' },
{ label: 'Custom Goal', value: 'custom', icon: 'pencil' },
];
return (
<View>
<Text variant="headlineMedium">Select Your Fitness Goals</Text>
<View style={{ flexDirection: 'row', flexWrap: 'wrap', marginTop: 16 }}>
{fitnessGoals.map((goal) => (
<Chip
key={goal.value}
onPress={() => updateRoutineData('goal', goal.value)}
selected={routineData.goal === goal.value}
style={[
{ margin: 4 },
routineData.goal === goal.value && { backgroundColor: theme.colors.primary },
]}
selectedColor="#fff"
textStyle={{ color: routineData.goal === goal.value ? '#fff' : theme.colors.primary }}
icon={() => (
<Icon
name={goal.icon}
size={20}
color={routineData.goal === goal.value ? '#fff' : theme.colors.primary}
/>
)}
>
{goal.label}
</Chip>
))}
</View>
{routineData.goal === 'custom' && (
<TextInput
label="Custom Goal"
value={routineData.customGoal}
onChangeText={(text) => updateRoutineData('customGoal', text)}
style={{ marginTop: 16 }}
mode="outlined"
placeholder="Enter your custom goal"
/>
)}
</View>
);
};
export default FitnessGoalsStep;
```
**`ScheduleStep.js`**
```javascript
// ScheduleStep.js
import React from 'react';
import { View, Text } from 'react-native';
import { useTheme } from 'react-native-paper';
import CustomPicker from './CustomPicker';
import DaySelector from './DaySelector';
const ScheduleStep = ({ routineData, updateSchedule }) => {
const theme = useTheme();
const frequencyOptions = Array.from({ length: 7 }, (_, i) => ({
label: `${i + 1} day${i > 0 ? 's' : ''} per week`,
value: (i + 1).toString(),
}));
const durationOptions = [
{ label: '30 minutes', value: '30' },
{ label: '45 minutes', value: '45' },
{ label: '60 minutes', value: '60' },
{ label: '90 minutes', value: '90' },
];
const timeOfDayOptions = [
{ label: 'Morning', value: 'morning' },
{ label: 'Afternoon', value: 'afternoon' },
{ label: 'Evening', value: 'evening' },
];
const intensityOptions = [
{ label: 'Low', value: 'low' },
{ label: 'Medium', value: 'medium' },
{ label: 'High', value: 'high' },
];
return (
<View>
<Text variant="headlineMedium">Choose Your Workout Schedule</Text>
<CustomPicker
label="Workout Frequency"
value={routineData.schedule.frequency}
onValueChange={(value) => updateSchedule('frequency', value)}
items={frequencyOptions}
placeholder="Select workout frequency"
/>
<CustomPicker
label="Workout Duration"
value={routineData.schedule.duration}
onValueChange={(value) => updateSchedule('duration', value)}
items={durationOptions}
placeholder="Select workout duration"
/>
<Text style={{ marginTop: 16 }}>Preferred Days</Text>
<DaySelector
selectedDays={routineData.schedule.preferredDays}
toggleDay={(day) => {
const updatedDays = routineData.schedule.preferredDays.includes(day)
? routineData.schedule.preferredDays.filter((d) => d !== day)
: [...routineData.schedule.preferredDays, day];
updateSchedule('preferredDays', updatedDays);
}}
/>
<CustomPicker
label="Time of Day"
value={routineData.schedule.timeOfDay}
onValueChange={(value) => updateSchedule('timeOfDay', value)}
items={timeOfDayOptions}
placeholder="Select time of day"
/>
<CustomPicker
label="Intensity"
value={routineData.schedule.intensity}
onValueChange={(value) => updateSchedule('intensity', value)}
items={intensityOptions}
placeholder="Select intensity level"
/>
</View>
);
};
export default ScheduleStep;
```
**`ExercisesStep.js`**
```javascript
// ExercisesStep.js
import React from 'react';
import { View, Text } from 'react-native';
import { useTheme } from 'react-native-paper';
const ExercisesStep = ({ routineData, updateRoutineData }) => {
const theme = useTheme();
return (
<View>
<Text variant="headlineMedium">Select Your Exercises</Text>
{/* Placeholder for exercise selection UI */}
<Text style={{ color: theme.colors.text, marginTop: 16 }}>
Implement exercise selection here. You can include search functionality,
categories, and selection of multiple exercises.
</Text>
</View>
);
};
export default ExercisesStep;
```
**`RestRecoveryStep.js`**
```javascript
// RestRecoveryStep.js
import React from 'react';
import { View, Text } from 'react-native';
import { TextInput, useTheme } from 'react-native-paper';
import DaySelector from './DaySelector';
const RestRecoveryStep = ({ routineData, updateRest }) => {
const theme = useTheme();
return (
<View>
<Text variant="headlineMedium">Configure Rest & Recovery</Text>
<Text style={{ marginTop: 16 }}>Rest Days</Text>
<DaySelector
selectedDays={routineData.rest.restDays}
toggleDay={(day) => {
const updatedDays = routineData.rest.restDays.includes(day)
? routineData.rest.restDays.filter((d) => d !== day)
: [...routineData.rest.restDays, day];
updateRest('restDays', updatedDays);
}}
/>
<Text style={{ marginTop: 16 }}>Cooldown Routine</Text>
{/* Placeholder for cooldown routine selection */}
<Text style={{ color: theme.colors.text, marginTop: 8 }}>
Implement cooldown routine selection here. You can include predefined cooldowns or allow users to create their own.
</Text>
<TextInput
label="Rest Between Sets (seconds)"
value={routineData.rest.restBetweenSets}
onChangeText={(text) => updateRest('restBetweenSets', text)}
style={{ marginTop: 16 }}
mode="outlined"
keyboardType="numeric"
placeholder="e.g., 30"
/>
</View>
);
};
export default RestRecoveryStep;
```
**`ReviewStep.js`**
```javascript
// ReviewStep.js
import React from 'react';
import { View } from 'react-native';
import { Text, Card, useTheme } from 'react-native-paper';
const ReviewStep = ({ routineData, fitnessGoals }) => {
const theme = useTheme();
const capitalizeFirstLetter = (string) => {
if (!string) return '';
return string.charAt(0).toUpperCase() + string.slice(1);
};
return (
<View>
<Text variant="headlineMedium">Review Your Routine</Text>
<Card style={{ marginTop: 16 }}>
<Card.Title title="Routine Name" />
<Card.Content>
<Text>{routineData.routineName || 'N/A'}</Text>
</Card.Content>
</Card>
<Card style={{ marginTop: 16 }}>
<Card.Title title="Fitness Goal" />
<Card.Content>
<Text>
{routineData.goal === 'custom'
? routineData.customGoal
: fitnessGoals.find((goal) => goal.value === routineData.goal)?.label || 'N/A'}
</Text>
</Card.Content>
</Card>
<Card style={{ marginTop: 16 }}>
<Card.Title title="Schedule" />
<Card.Content>
<Text>Frequency: {routineData.schedule.frequency} day(s)/week</Text>
<Text>Duration: {routineData.schedule.duration} minutes</Text>
<Text>Preferred Days: {routineData.schedule.preferredDays.join(', ') || 'N/A'}</Text>
<Text>Time of Day: {capitalizeFirstLetter(routineData.schedule.timeOfDay) || 'N/A'}</Text>
<Text>Intensity: {capitalizeFirstLetter(routineData.schedule.intensity) || 'N/A'}</Text>
</Card.Content>
</Card>
{/* Add Cards for Exercises and Rest & Recovery as they are implemented */}
</View>
);
};
export default ReviewStep;
```
#### 3. Update the Main `RoutineCreationScreen`
Now, update the main `RoutineCreationScreen` to utilize the new modular components and dynamic step configuration.
**`RoutineCreationScreen.js`**
```javascript
// RoutineCreationScreen.js
import React, { useState, useContext } from 'react';
import { ScrollView, Alert, View } from 'react-native';
import { Text, Button, ProgressBar, useTheme } from 'react-native-paper';
import { AuthContext } from '../../context/AuthContext';
import getStyles from './RoutineCreationStyles';
import FitnessGoalsStep from './FitnessGoalsStep';
import ScheduleStep from './ScheduleStep';
import ExercisesStep from './ExercisesStep';
import RestRecoveryStep from './RestRecoveryStep';
import ReviewStep from './ReviewStep';
const STEPS = {
FITNESS_GOALS: 0,
SCHEDULE: 1,
EXERCISES: 2,
REST_RECOVERY: 3,
REVIEW: 4,
};
const RoutineCreationScreen = ({ navigation }) => {
const theme = useTheme();
const styles = getStyles(theme);
const { user } = useContext(AuthContext);
const [currentStep, setCurrentStep] = useState(STEPS.FITNESS_GOALS);
const [routineData, setRoutineData] = useState({
routineName: '',
goal: '',
customGoal: '',
schedule: {
frequency: '',
duration: '',
preferredDays: [],
timeOfDay: '',
intensity: '',
},
exercises: [],
rest: {
restDays: [],
cooldownRoutine: [],
restBetweenSets: '',
},
});
const fitnessGoals = [
{ label: 'Build Muscle', value: 'muscle', icon: 'dumbbell' },
{ label: 'Lose Weight', value: 'weight', icon: 'scale' },
{ label: 'Increase Flexibility', value: 'flexibility', icon: 'yoga' },
{ label: 'Improve Endurance', value: 'endurance', icon: 'run' },
{ label: 'Toning', value: 'toning', icon: 'human' },
{ label: 'General Fitness', value: 'general', icon: 'heart-pulse' },
{ label: 'Custom Goal', value: 'custom', icon: 'pencil' },
];
const steps = [
{
title: 'Fitness Goals',
component: (
<FitnessGoalsStep
routineData={routineData}
updateRoutineData={(field, value) =>
setRoutineData((prev) => ({
...prev,
[field]: value,
}))
}
/>
),
validate: () => {
if (!routineData.goal) {
Alert.alert('Validation Error', 'Please select a fitness goal.');
return false;
}
if (routineData.goal === 'custom' && !routineData.customGoal.trim()) {
Alert.alert('Validation Error', 'Please enter your custom goal.');
return false;
}
return true;
},
},
{
title: 'Schedule',
component: (
<ScheduleStep
routineData={routineData}
updateSchedule={(field, value) =>
setRoutineData((prev) => ({
...prev,
schedule: {
...prev.schedule,
[field]: value,
},
}))
}
/>
),
validate: () => {
const { frequency, duration, preferredDays, timeOfDay, intensity } = routineData.schedule;
if (!frequency || !duration || preferredDays.length === 0 || !timeOfDay || !intensity) {
Alert.alert('Validation Error', 'Please complete all schedule fields.');
return false;
}
return true;
},
},
{
title: 'Exercises',
component: (
<ExercisesStep
routineData={routineData}
updateRoutineData={(field, value) =>
setRoutineData((prev) => ({
...prev,
[field]: value,
}))
}
/>
),
validate: () => {
if (routineData.exercises.length === 0) {
Alert.alert('Validation Error', 'Please select at least one exercise.');
return false;
}
return true;
},
},
{
title: 'Rest & Recovery',
component: (
<RestRecoveryStep
routineData={routineData}
updateRest={(field, value) =>
setRoutineData((prev) => ({
...prev,
rest: {
...prev.rest,
[field]: value,
},
}))
}
/>
),
validate: () => {
const { restDays, restBetweenSets } = routineData.rest;
if (restDays.length === 0 || !restBetweenSets) {
Alert.alert('Validation Error', 'Please complete all rest & recovery fields.');
return false;
}
return true;
},
},
{
title: 'Review',
component: (
<ReviewStep
routineData={routineData}
fitnessGoals={fitnessGoals}
/>
),
validate: () => {
if (!routineData.routineName.trim()) {
Alert.alert('Validation Error', 'Please provide a name for your routine.');
return false;
}
return true;
},
},
];
const handleNext = () => {
if (!steps[currentStep].validate()) return;
if (currentStep < STEPS.REVIEW) {
setCurrentStep(currentStep + 1);
} else {
// Handle routine creation logic here
Alert.alert('Success', 'Routine created successfully!');
navigation.goBack();
}
};
const handleBack = () => {
if (currentStep > STEPS.FITNESS_GOALS) {
setCurrentStep(currentStep - 1);
} else {
navigation.goBack();
}
};
const renderStepContent = () => {
return steps[currentStep].component;
};
return (
<ScrollView style={styles.container} contentContainerStyle={styles.contentContainer}>
<ProgressBar
progress={(currentStep + 1) / steps.length}
color={theme.colors.primary}
style={styles.progressBar}
/>
{renderStepContent()}
<View style={styles.navigationContainer}>
<Button
mode="outlined"
onPress={handleBack}
style={styles.navButton}
color={theme.colors.primary}
>
{currentStep === STEPS.FITNESS_GOALS ? 'Cancel' : 'Back'}
</Button>
{currentStep === STEPS.REVIEW ? (
<Button
mode="contained"
onPress={handleNext}
style={styles.navButton}
color={theme.colors.primary}
>
Create Routine
</Button>
) : (
<Button
mode="contained"
onPress={handleNext}
style={styles.navButton}
color={theme.colors.primary}
>
Next
</Button>
)}
</View>
</ScrollView>
);
};
export default RoutineCreationScreen;
```
#### 4. Update Styles
Ensure that your styles are updated to accommodate the refactored components. Here's an example of how you might structure your `RoutineCreationStyles.js`.
**`RoutineCreationStyles.js`**
```javascript
// RoutineCreationStyles.js
import { StyleSheet } from 'react-native';
const getStyles = (theme) =>
StyleSheet.create({
container: {
flex: 1,
backgroundColor: theme.colors.background,
padding: 16,
},
contentContainer: {
paddingBottom: 24,
},
progressBar: {
height: 8,
borderRadius: 4,
marginBottom: 24,
},
navigationContainer: {
flexDirection: 'row',
justifyContent: 'space-between',
marginTop: 24,
},
navButton: {
flex: 1,
marginHorizontal: 4,
},
// Add any additional styles if needed
});
export default getStyles;
```
#### 5. Summary of Changes
- **Modularization:** Each step is now a separate component (`FitnessGoalsStep`, `ScheduleStep`, `ExercisesStep`, `RestRecoveryStep`, `ReviewStep`), enhancing clarity and maintainability.
- **Dynamic Steps:** The steps are defined in a configuration array, making it easy to add, remove, or reorder steps without modifying the core logic.
- **Reusable Components:** `CustomPicker` and `DaySelector` are created to handle common UI patterns, reducing code duplication.
- **Validation Logic:** Each step has its own `validate` function within the steps configuration, streamlining the validation process.
- **State Management:** State updates are handled in a more granular and organized manner, improving readability.
This refactored approach ensures that your `RoutineCreationScreen` is both dynamic and maintainable, allowing for easier future enhancements and scalability.