diff --git a/mobile/components/CreateProgram.js b/mobile/components/CreateProgram.js
index 618721a..32ddbfd 100644
--- a/mobile/components/CreateProgram.js
+++ b/mobile/components/CreateProgram.js
@@ -1,51 +1,241 @@
-import React, { useState } from 'react';
-import { StyleSheet, Text, View, TextInput, Button, FlatList, TouchableOpacity, KeyboardAvoidingView } from 'react-native';
+import React, { useState, useEffect} from 'react';
+import { ScrollView,StyleSheet, Text, View, TextInput, TouchableOpacity, Pressable, FlatList, Modal } from 'react-native';
+import { Picker } from '@react-native-picker/picker'; // Using Picker for the dropdown
+import apiInstance from "../Api";
+import { useQuery } from "@tanstack/react-query";
+import { useSelector } from 'react-redux';
+import { userSessionToken } from '../user.js';
+import Toast from 'react-native-toast-message';
+import SpinboxInput from "./common/SpinboxInput";
-const CreateProgram = ({ darkMode, setSelectedPage }) => {
+const CreateProgram = ({ darkMode }) => {
const styles = darkMode ? darkStyles : lightStyles;
const [title, setTitle] = useState('');
const [description, setDescription] = useState('');
- const [labels, setLabels] = useState([]);
- const [labelText, setLabelText] = useState('');
- const [exercises, setExercises] = useState([]);
- const [exercise, setExercise] = useState({ name: '', repetitions: '', sets: '' });
-
- const handleProgramCreation = () => {
- const newProgram = { title, description, labels, exercises };
- console.log('Creating program:', newProgram);
- // Add your logic to handle program submission
+ const [weeks, setWeeks] = useState([]);
+ const [exerciseModalVisible, setExerciseModalVisible] = useState(false);
+ const [selectedWeekIndex, setSelectedWeekIndex] = useState(null);
+ const [selectedWorkoutIndex, setSelectedWorkoutIndex] = useState(null);
+ const [selectedExercise, setSelectedExercise] = useState('');
+ const [sets, setSets] = useState('');
+ const [type, setType] = useState('BODY_BUILDING');
+ const [level, setLevel] = useState('BEGINNER');
+ const [interval, setInterval] = useState(0);
+
+ const [reps, setReps] = useState('');
+
+ const SpinBoxComponent = () => {
+
+ return (
+
+
+
+ );
};
+ const [exerciseOptions, setExerciseOptions] = useState([])
- const addLabel = () => {
- if (labelText.trim()) {
- setLabels([...labels, labelText.trim()]);
- setLabelText('');
- }
+ const {
+ data: exercisesData,
+ isFetching: exercisesIsFetching,
+ isLoading: exercisesIsLoading,
+ } = useQuery({
+ queryKey: ['exercises'],
+ queryFn: async () => {
+ const response = await apiInstance(sessionToken).get('api/exercises')
+
+ return response.data
+ },
+ refetchOnWindowFocus: false,
+ })
+
+ useEffect(() => {
+ if (exercisesData && !exercisesIsFetching) {
+ setExerciseOptions(exercisesData)
+ }
+ }, [exercisesData, exercisesIsFetching])
+
+ const sessionToken = useSelector(userSessionToken)
+
+ /*const exerciseOptions = [
+ { id: 1, name: 'push-up' },
+ { id: 2, name: 'pull-up' },
+ ];*/
+ const types = [
+ { id: 0, label: 'Body Building', value:'BODY_BUILDING' }
+
+ ];
+ const levels = [
+ { id: 0, label: 'Beginner', value:'BEGINNER' },
+ { id: 1, label: 'Intermediate', value:'INTERMEDIATE' },
+ { id: 2, label: 'Professional', value:'PROFESSIONAL' }
+ ];
+
+ // Add Week
+ const addWeek = () => {
+ setWeeks([...weeks, { workouts: [] }]);
};
- const removeLabel = (index) => {
- setLabels(labels.filter((_, i) => i !== index));
+ // Remove Week
+ const removeWeek = (index) => {
+ setWeeks(weeks.filter((_, i) => i !== index));
};
+ // Add Workout
+ const addWorkout = (weekIndex) => {
+ setWeeks((prevWeeks) =>
+ prevWeeks.map((week, i) =>
+ i === weekIndex
+ ? {
+ ...week,
+ workouts: [...week.workouts, { name: '', exercises: [] }],
+ }
+ : week
+ )
+ );
+ };
+
+ // Remove Workout
+ const removeWorkout = (weekIndex, workoutIndex) => {
+ const updatedWeeks = [...weeks];
+ updatedWeeks[weekIndex].workouts = updatedWeeks[weekIndex].workouts.filter(
+ (_, i) => i !== workoutIndex
+ );
+ setWeeks(updatedWeeks);
+ };
+
+ const clearFields = () => {
+ setTitle('');
+ setDescription('');
+ setWeeks([]);
+ setType('');
+ setSelectedExercise('');
+ setSets('');
+ setReps('');
+ };
+
+ const handleProgramCreation = async () => {
+ /*const newProgram = { title, description, labels, exercises };
+ console.log('Creating program:', newProgram);*/
+ console.log(title);
+ console.log(description);
+ console.log(type);
+ console.log(level);
+ console.log(interval);
+ weeks.forEach((week)=>{
+ console.log("Week "+weeks.indexOf(week));
+ console.log("Workout count: "+week.workouts.length);
+ week.workouts.forEach((workout)=>{
+ console.log("Workout "+week.workouts.indexOf(workout));
+ console.log("Workout name: "+workout.name);
+ console.log("Exercise count: "+workout.exercises.length);
+ workout.exercises.forEach((exercise)=>{
+ console.log("Exercise "+exercise.exerciseId);
+ console.log("Sets: "+exercise.sets);
+ console.log("Reps: "+exercise.repetitions);
+
+ });
+ });
+ });
+
+ if (!title || !description || weeks.length === 0 || weeks.some((item)=>{return item.workouts.length==0||item.workouts.some((workout)=>{return workout.name.length == 0 || workout.exercises.length==0})})) {
+ Toast.show({
+ type: 'error',
+ position: 'bottom',
+ text1: 'Create Program Error',
+ text2: 'Fill all the fields to create a program.',
+ visibilityTime: 2000,
+ autoHide: true,
+ topOffset: 30,
+ bottomOffset: 40
+ });
+ return;
+ }
+
+ const response = await apiInstance(sessionToken).post('api/training-programs', {
+ title,
+ description,
+ type,
+ level,
+ interval,
+ weeks
+ });
+
+ if (response.status === 201) {
+ Toast.show({
+ type: 'success',
+ position: 'bottom',
+ text1: 'Program Created',
+ text2: 'Successfully created the program.',
+ visibilityTime: 2000,
+ autoHide: true,
+ topOffset: 30,
+ bottomOffset: 40
+ });
+ clearFields();
+ }
+ else{
+ Toast.show({
+ type: 'error',
+ position: 'bottom',
+ text1: 'Create Program Error',
+ text2: 'There was an error while creating the program. Please try again.',
+ visibilityTime: 2000,
+ autoHide: true,
+ topOffset: 30,
+ bottomOffset: 40
+ });
+ return;
+
+ }
+ };
+ const handleWorkoutNameChange = (weekIndex, workoutIndex, newName) => {
+ setWeeks((prevWeeks) =>
+ prevWeeks.map((week, i) =>
+ i === weekIndex
+ ? {
+ ...week,
+ workouts: week.workouts.map((workout, j) =>
+ j === workoutIndex ? { ...workout, name: newName } : workout
+ ),
+ }
+ : week
+ )
+ );
+ };
+ // Add Exercise
const addExercise = () => {
- if (exercise.name.trim() && exercise.repetitions && exercise.sets) {
- setExercises([...exercises, exercise]);
- setExercise({ name: '', repetitions: '', sets: '' });
+ if (selectedExercise && sets && reps) {
+ const updatedWeeks = [...weeks];
+ const selectedExerciseId = exerciseOptions.find((element)=>element.name===selectedExercise).id;
+ updatedWeeks[selectedWeekIndex].workouts[selectedWorkoutIndex].exercises.push({
+ exerciseId: selectedExerciseId,
+ sets,
+ repetitions:reps,
+ });
+ setWeeks(updatedWeeks);
+ setSelectedExercise('');
+ setSets('');
+ setReps('');
+ setExerciseModalVisible(false);
}
};
- const removeExercise = (index) => {
- setExercises(exercises.filter((_, i) => i !== index));
+ // Remove Exercise
+ const removeExercise = (weekIndex, workoutIndex, exerciseIndex) => {
+ const updatedWeeks = [...weeks];
+ updatedWeeks[weekIndex].workouts[workoutIndex].exercises = updatedWeeks[
+ weekIndex
+ ].workouts[workoutIndex].exercises.filter((_, i) => i !== exerciseIndex);
+ setWeeks(updatedWeeks);
};
return (
-
+
Create New Program
@@ -53,362 +243,326 @@ const CreateProgram = ({ darkMode, setSelectedPage }) => {
-
-
-
- Add
-
+ setType(value)}
+ >
+ {types.map((item) => (
+
+ ))}
+
+
+ setLevel(value)}
+ >
+ {levels.map((item) => (
+
+ ))}
+
+
+ Select Interval
+
+
index.toString()}
- renderItem={({ item, index }) => (
-
- {item}
- removeLabel(index)}>
- ✕
+ data={weeks}
+ keyExtractor={(_, index) => index.toString()}
+ renderItem={({ item: week, index: weekIndex }) => (
+
+ Week {weekIndex + 1}
+ addWorkout(weekIndex)}
+ >
+ Add Workout
-
- )}
- horizontal
- showsHorizontalScrollIndicator={false}
- />
- Exercises
-
-
- setExercise({ ...exercise, name: text })}
- />
- setExercise({ ...exercise, repetitions: text })}
- />
- setExercise({ ...exercise, sets: text })}
- />
-
- Add
-
-
+ i.toString()}
+ renderItem={({ item: workout, index: workoutIndex }) => (
+
+
+ Workout {workoutIndex + 1}
+
+ handleWorkoutNameChange(weekIndex, workoutIndex, text)}
+ />
- index.toString()}
- renderItem={({ item, index }) => (
-
-
- {item.name} - {item.repetitions} reps x {item.sets} sets
-
- removeExercise(index)}>
- ✕
+ {
+ setSelectedWeekIndex(weekIndex);
+ setSelectedWorkoutIndex(workoutIndex);
+ setExerciseModalVisible(true);
+ }}
+ >
+ Add Exercise
+
+
+ i.toString()}
+ renderItem={({ item: exercise, index: exerciseIndex }) => (
+
+
+ {exerciseOptions.find((i)=>i.id===exercise.exerciseId).name} - {exercise.repetitions} reps x {exercise.sets}{' '}
+ sets
+
+
+ removeExercise(
+ weekIndex,
+ workoutIndex,
+ exerciseIndex
+ )
+ }
+ >
+ ✕
+
+
+ )}
+ />
+ removeWorkout(weekIndex, workoutIndex)}
+ >
+ Remove Workout
+
+
+ )}
+ />
+ removeWeek(weekIndex)}>
+ Remove Week
)}
/>
+
+
+ Add Week
+
+
+
+ setExerciseModalVisible(false)}
+ >
+
+
+ Add Exercise
+ setSelectedExercise(value)}
+ >
+
+ {exerciseOptions.map((exercise) => (
+
+ ))}
+
+
+
+
+
+
+
+
+
+ Add
+
+
+ setExerciseModalVisible(false)}>
+ Cancel
+
+
+
+
+
- Create Program
+ Create Program
-
+
);
};
-// Reusing similar styles from CreatePost component with additional styling for exercises
const lightStyles = StyleSheet.create({
container: {
- padding: 20,
- backgroundColor: '#f5f5f5',
- flex: 1,
- },
- title: {
- fontSize: 26,
- fontWeight: 'bold',
- color: '#333',
- marginBottom: 15,
- },
- input: {
- height: 50,
- backgroundColor: '#fff',
- borderRadius: 10,
- paddingHorizontal: 15,
- marginBottom: 10,
- fontSize: 16,
- color: '#333',
- shadowColor: '#000',
- shadowOpacity: 0.1,
- shadowRadius: 5,
- elevation: 3,
- },
- descriptionInput: {
- height: 100,
- textAlignVertical: 'top',
- },
- labelContainer: {
- flexDirection: 'row',
- alignItems: 'center',
- marginBottom: 10,
- },
- labelInput: {
- flex: 1,
- height: 50,
- backgroundColor: '#fff',
- borderRadius: 10,
- paddingHorizontal: 15,
- fontSize: 16,
- shadowColor: '#000',
- shadowOpacity: 0.1,
- shadowRadius: 5,
- elevation: 3,
- },
- addButton: {
- marginLeft: 10,
- paddingVertical: 12,
- paddingHorizontal: 16,
- backgroundColor: '#007bff',
- borderRadius: 10,
- elevation: 3,
- },
- addButtonText: {
- color: '#fff',
- fontWeight: 'bold',
- },
- labelItem: {
- flexDirection: 'row',
- alignItems: 'center',
- backgroundColor: '#e0e0e0',
- paddingHorizontal: 10,
- paddingVertical: 5,
- borderRadius: 15,
- marginRight: 5,
- },
- labelItemText: {
- fontSize: 14,
- color: '#333',
- },
- removeLabel: {
- color: '#ff3b30',
- marginLeft: 5,
- fontWeight: 'bold',
- },
- sectionTitle: {
- fontSize: 20,
- fontWeight: 'bold',
- marginVertical: 10,
- color: '#333',
- },
- exerciseContainer: {
- flexDirection: 'row',
- alignItems: 'center',
- marginBottom: 10,
- },
- exerciseInput: {
- flex: 1,
- height: 50,
- backgroundColor: '#fff',
- borderRadius: 10,
- paddingHorizontal: 15,
- fontSize: 16,
- marginRight: 5,
- shadowColor: '#000',
- shadowOpacity: 0.1,
- shadowRadius: 5,
- elevation: 3,
- },
- exerciseItem: {
- flexDirection: 'row',
- justifyContent: 'space-between',
- alignItems: 'center',
- paddingVertical: 5,
- backgroundColor: '#e0e0e0',
- borderRadius: 10,
- paddingHorizontal: 15,
- marginVertical: 5,
- },
- exerciseItemText: {
- fontSize: 14,
- color: '#333',
- },
- postButton: {
- backgroundColor: '#007bff',
- paddingVertical: 15,
- borderRadius: 10,
- alignItems: 'center',
- marginTop: 20,
- shadowColor: '#007bff',
- shadowOpacity: 0.5,
- shadowRadius: 5,
- elevation: 5,
- },
- postButtonText: {
- color: '#fff',
- fontSize: 16,
- fontWeight: 'bold',
- },
- placeholderColor: 'gray',
-});
+ padding: 20,
+ backgroundColor: '#f5f5f5',
+ flex: 1,
+ },
+
+ container: {
+ padding: 10,
+ flex: 1,
+ },
+ title: {
+ fontSize: 26,
+ fontWeight: 'bold',
+ color: '#333',
+ marginBottom: 15,
+ },
+ input: {
+ height: 50,
+ backgroundColor: '#fff',
+ borderRadius: 10,
+ paddingHorizontal: 15,
+ marginBottom: 10,
+ fontSize: 16,
+ color: '#333',
+ shadowColor: '#000',
+ shadowOpacity: 0.1,
+ shadowRadius: 5,
+ elevation: 3,
+ },
+ descriptionInput: {
+ height: 100,
+ textAlignVertical: 'top',
+ },
-const darkStyles = StyleSheet.create({
- container: {
- padding: 20,
- backgroundColor: '#121212',
- flex: 1,
- },
- title: {
- fontSize: 26,
- fontWeight: 'bold',
- color: '#ffffff',
- marginBottom: 15,
- },
- input: {
- height: 50,
- backgroundColor: '#333333',
- borderRadius: 10,
- paddingHorizontal: 15,
- marginBottom: 10,
- fontSize: 16,
- color: '#ffffff',
- shadowColor: '#000',
- shadowOpacity: 0.2,
- shadowRadius: 5,
- elevation: 3,
- },
- descriptionInput: {
- height: 100,
- textAlignVertical: 'top',
- },
- labelContainer: {
- flexDirection: 'row',
- alignItems: 'center',
- marginBottom: 10,
- },
- labelInput: {
- flex: 1,
- height: 50,
- backgroundColor: '#333333',
- borderRadius: 10,
- paddingHorizontal: 15,
- fontSize: 16,
- color: '#ffffff',
- shadowColor: '#000',
- shadowOpacity: 0.2,
- shadowRadius: 5,
- elevation: 3,
- },
addButton: {
- marginLeft: 10,
- paddingVertical: 12,
- paddingHorizontal: 16,
- backgroundColor: '#007bff',
- borderRadius: 10,
- elevation: 3,
- },
- addButtonText: {
- color: '#ffffff',
- fontWeight: 'bold',
- },
- labelItem: {
- flexDirection: 'row',
- alignItems: 'center',
- backgroundColor: '#555555',
- paddingHorizontal: 10,
- paddingVertical: 5,
- borderRadius: 15,
- marginRight: 5,
- },
- labelItemText: {
- fontSize: 14,
- color: '#ffffff',
- },
- removeLabel: {
- color: '#ff3b30',
- marginLeft: 5,
- fontWeight: 'bold',
- },
- sectionTitle: {
- fontSize: 20,
- fontWeight: 'bold',
- marginVertical: 10,
- color: '#ffffff',
- },
- exerciseContainer: {
- flexDirection: 'row',
- alignItems: 'center',
- marginBottom: 10,
- },
- exerciseInput: {
- flex: 1,
- height: 50,
- backgroundColor: '#333333',
- borderRadius: 10,
- paddingHorizontal: 15,
- fontSize: 16,
- color: '#ffffff',
- marginRight: 5,
- shadowColor: '#000',
- shadowOpacity: 0.2,
- shadowRadius: 5,
- elevation: 3,
- },
- exerciseItem: {
- flexDirection: 'row',
- justifyContent: 'space-between',
- alignItems: 'center',
- paddingVertical: 5,
- backgroundColor: '#555555',
- borderRadius: 10,
- paddingHorizontal: 15,
- marginVertical: 5,
- },
- exerciseItemText: {
- fontSize: 14,
- color: '#ffffff',
- },
- postButton: {
- backgroundColor: '#007bff',
- paddingVertical: 15,
- borderRadius: 10,
- alignItems: 'center',
- marginTop: 20,
- shadowColor: '#007bff',
- shadowOpacity: 0.6,
- shadowRadius: 5,
- elevation: 5,
- },
- postButtonText: {
- color: '#ffffff',
- fontSize: 16,
- fontWeight: 'bold',
- },
- placeholderColor: '#aaaaaa',
+ marginLeft: 10,
+ paddingVertical: 12,
+ paddingHorizontal: 16,
+ backgroundColor: '#007bff',
+ borderRadius: 10,
+ elevation: 3,
+ },
+ addButtonText: {
+ color: '#fff',
+ fontWeight: 'bold',
+ },
+ addWeekButton: {
+ marginLeft: 10,
+ paddingVertical: 12,
+ paddingHorizontal: 16,
+ backgroundColor: '#007bff',
+ borderRadius: 10,
+ alignItems: 'center',
+ width:100,
+ elevation: 3,
+ },
+ addWeekButtonContainer: {
+ alignItems: 'center',
+ },
+ postButton: {
+ backgroundColor: '#007bff',
+ paddingVertical: 15,
+ borderRadius: 10,
+ alignItems: 'center',
+ marginTop: 20,
+ shadowColor: '#007bff',
+ shadowOpacity: 0.5,
+ shadowRadius: 5,
+ elevation: 5,
+ },
+ postButtonText: {
+ color: '#fff',
+ fontSize: 16,
+ fontWeight: 'bold',
+ },
+ weekContainer: { marginBottom: 20 },
+ workoutContainer: { marginBottom: 15 },
+ sectionTitle: { fontSize: 18 },
+ exerciseContainer: {
+ flexDirection: 'row',
+ alignItems: 'center',
+ marginBottom: 10,
+ },
+ exerciseInput: {
+ flex: 1,
+ height: 50,
+ backgroundColor: '#fff',
+ borderRadius: 10,
+ paddingHorizontal: 15,
+ fontSize: 16,
+ marginRight: 5,
+ shadowColor: '#000',
+ shadowOpacity: 0.1,
+ shadowRadius: 5,
+ elevation: 3,
+ },
+ exerciseItem: {
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ alignItems: 'center',
+ paddingVertical: 5,
+ backgroundColor: '#e0e0e0',
+ borderRadius: 10,
+ paddingHorizontal: 15,
+ marginVertical: 5,
+ },
+ exerciseItemText: {
+ fontSize: 14,
+ color: '#333',
+ },
+ removeLabel: { color: 'red' },
+ modalContainer: {
+ flex: 1,
+ justifyContent: 'center',
+ alignItems: 'center',
+ backgroundColor: 'rgba(0, 0, 0, 0.5)',
+ },
+ modalContent: {
+ backgroundColor: '#fff',
+ borderRadius: 10,
+ padding: 20,
+ width: '90%',
+ alignItems: 'center',
+ },
+ modalTitle: {
+ fontSize: 20,
+ fontWeight: 'bold',
+ marginBottom: 15,
+ },
+ placeholderColor: 'gray',
+ picker: {
+ width: '100%',
+ height: 50,
+ backgroundColor: '#f0f0f0',
+ marginBottom: 15,
+ borderRadius: 5,
+ },
+ modalButtons: {
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ width: '100%',
+ },
+ cancelButton: {
+ backgroundColor: '#ff3b30',
+ paddingVertical: 12,
+ paddingHorizontal: 16,
+ borderRadius: 10,
+ },
+ cancelButtonText: {
+ color: '#fff',
+ fontWeight: 'bold',
+ },
});
-// Define darkStyles similarly as lightStyles with color changes for dark mode
-
export default CreateProgram;
diff --git a/mobile/components/common/SpinboxInput.js b/mobile/components/common/SpinboxInput.js
new file mode 100644
index 0000000..8f769c7
--- /dev/null
+++ b/mobile/components/common/SpinboxInput.js
@@ -0,0 +1,51 @@
+import React, { useState } from 'react';
+import { View, Text, StyleSheet, TouchableOpacity } from 'react-native';
+
+const SpinboxInput = ({onChange}) => {
+ const [value, setValue] = useState(0);
+
+ const handleIncrement = () => {
+const newValue = Math.min(value + 1, 2);
+ setValue(newValue);
+ onChange(newValue); };
+
+ const handleDecrement = () => {
+const newValue = Math.max(value - 1, 0);
+ setValue(newValue);
+ onChange(newValue); };
+
+ return (
+
+
+ -
+
+ {value}
+
+ +
+
+
+ );
+};
+
+const styles = StyleSheet.create({
+ container: {
+ flexDirection: 'row',
+ alignItems: 'center',
+ borderWidth: 1,
+ borderColor: '#ccc',
+ borderRadius: 5,
+ paddingHorizontal: 10,
+ },
+ button: {
+ padding: 8,
+ },
+ buttonText: {
+ fontSize: 20,
+ },
+ value: {
+ fontSize: 18,
+ marginHorizontal: 10,
+ },
+});
+
+export default SpinboxInput;
\ No newline at end of file