Skip to content

Commit

Permalink
Merge pull request #359 from bounswe/issue#332
Browse files Browse the repository at this point in the history
ask user to rest
  • Loading branch information
sametaln authored Dec 16, 2024
2 parents c113cd4 + 847dacc commit 827d3ba
Show file tree
Hide file tree
Showing 4 changed files with 178 additions and 17 deletions.
11 changes: 11 additions & 0 deletions frontend/src/components/ProgramFeedCard.component.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,17 @@ function ProgramFeedCard({
},
onError: (error) => {
console.log(error)
// If error status is 400, it means the user is already joined to a program with same interest area
if (error.response.status === 400) {
toast({
title: error.response.data.message,
status: 'error',
duration: 3000,
isClosable: true,
})
return
}

toast({
title: 'An error occurred.',
status: 'error',
Expand Down
53 changes: 53 additions & 0 deletions frontend/src/components/RestModal.component.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import React from 'react';
import {
Modal,
ModalOverlay,
ModalContent,
ModalHeader,
ModalFooter,
ModalBody,
ModalCloseButton,
Button,
Text,
} from '@chakra-ui/react';

function RestModal({
onClose,
isOpen,
interval, // Number of days for the recommended rest period
onContinueWorkout, // Callback to handle when the user wants to continue the workout
}) {
return (
<Modal isOpen={isOpen} onClose={onClose}>
<ModalOverlay />
<ModalContent>
<ModalHeader>
Recommended Rest Period
</ModalHeader>
<ModalCloseButton />
<ModalBody>
<Text mb={4}>
It is recommended to rest for <strong>{interval} day{interval > 1 ? 's' : ''}</strong> before continuing your workouts. This will help you recover and maximize your performance.
</Text>
<Text>
If you'd like, you can choose to continue the workout without waiting.
</Text>
</ModalBody>

<ModalFooter>
<Button colorScheme="gray" mr={3} onClick={onClose}>
Close
</Button>
<Button colorScheme="purple" onClick={() => {
onContinueWorkout();
onClose();
}}>
Continue Anyway
</Button>
</ModalFooter>
</ModalContent>
</Modal>
);
}

export default RestModal;
50 changes: 48 additions & 2 deletions frontend/src/components/Training.component.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import {
} from '@chakra-ui/react';
import { useContext } from 'react';
import { UserContext } from '../context/UserContext';
import RestModal from './RestModal.component';

const renderRatingStars = (rating, ratingCount) => {
return (
Expand Down Expand Up @@ -97,11 +98,18 @@ const TrainingCard = () => {
const toast = useToast();

const { isOpen, onOpen, onClose } = useDisclosure();

const {
isOpen: isWorkoutOpen,
onOpen: onWorkoutOpen,
onClose: onWorkoutClose
} = useDisclosure();

const {
isOpen: isRestOpen,
onOpen: onRestOpen,
onClose: onRestClose
} = useDisclosure();
const [weekNumber, setWeekNumber] = useState(null);
const [workoutNumber, setWorkoutNumber] = useState(null);
const [selectedExerciseId, setSelectedExerciseId] = useState(null);
Expand Down Expand Up @@ -266,6 +274,30 @@ const TrainingCard = () => {
return week.workouts.every(workout => isWorkoutComplete(workout));
};

const shouldUserRest = (restIntervalOnDays, lastWorkoutDate) => {
if (!lastWorkoutDate) {
return false;
}

const lastWorkoutDateTime = new Date(lastWorkoutDate).getTime();
const timeSinceLastWorkout = Date.now() - lastWorkoutDateTime;
const timeSinceLastWorkoutInDays = timeSinceLastWorkout / (1000 * 60 * 60 * 24);

return timeSinceLastWorkoutInDays < restIntervalOnDays;
};

const remainingRestDays = (restIntervalOnDays, lastWorkoutDate) => {
if (!lastWorkoutDate) {
return 0;
}

const lastWorkoutDateTime = new Date(lastWorkoutDate).getTime();
const timeSinceLastWorkout = Date.now() - lastWorkoutDateTime;
const remainingDays = restIntervalOnDays - (timeSinceLastWorkout / (1000 * 60 * 60 * 24));

return Math.ceil(remainingDays);
};

return (
<div className="w-full max-w-[60%] mx-auto p-4 bg-white shadow-lg rounded-lg text-sm">

Expand Down Expand Up @@ -503,7 +535,14 @@ const TrainingCard = () => {
</Text>
) : canStartExercise ? (
<Button
onClick={() => handleStartSession(exerciseob.id)}
onClick={() => {
if (shouldUserRest(trainingProgram.interval, trainingProgram.lastCompletedWorkoutDate)) {
setSelectedExerciseId(exerciseob.id);
onRestOpen();
} else {
handleStartSession(exerciseob.id)
}
}}
colorScheme="green"
size="sm"
>
Expand Down Expand Up @@ -558,7 +597,14 @@ const TrainingCard = () => {
data={trainingProgram}
weekNumber={weekNumber}
workoutNumber={workoutNumber}

/>
<RestModal
isOpen={isRestOpen}
onClose={onRestClose}
interval={remainingRestDays(trainingProgram.interval, trainingProgram.lastCompletedWorkoutDate)}
onContinueWorkout={() => {
handleStartSession(selectedExerciseId);
}}
/>
</div>
);
Expand Down
81 changes: 66 additions & 15 deletions frontend/src/components/UserJoinedProgramsCard.component.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,13 @@ import {
Spinner,
useToast
} from '@chakra-ui/react';

import Detailed_Ex_Modal from './Detailed_Ex_Modal.component';
import { useDisclosure } from '@chakra-ui/react';
import { InfoIcon, StarIcon, RepeatIcon, ArrowRightIcon, SettingsIcon } from '@chakra-ui/icons';
import { useContext } from 'react';
import { UserContext } from '../context/UserContext';
import RestModal from './RestModal.component';
import { useQuery } from '@tanstack/react-query';

const UserJoinedProgramsCard = () => {
const sessionToken = useSelector(userSessionToken);
Expand All @@ -37,15 +38,17 @@ const UserJoinedProgramsCard = () => {
const [selectedExerciseId, setSelectedExerciseId] = useState(null);
const [selectedProgram, setSelectedProgram] = useState(null);

useEffect(() => {
const fetchJoinedPrograms = async () => {
const { isOpen: isRestOpen, onOpen: onRestOpen, onClose: onRestClose } = useDisclosure();

// Fetch joined programs
const { data: joinedProgramsData, isLoading: joinedProgramsIsLoading } = useQuery({
queryKey: ['joinedPrograms'],
queryFn: async () => {
if (!user) {
setLoading(false);
return;
return [];
}

try {
setLoading(true);
const response = await apiInstance(sessionToken).get(
`/api/training-programs/joined/${user.username}`
);
Expand All @@ -59,7 +62,7 @@ const UserJoinedProgramsCard = () => {
)
);

setJoinedPrograms(programsWithUncompletedExercises);
return programsWithUncompletedExercises;
} catch (error) {
console.error('Error fetching joined programs:', error);
toast({
Expand All @@ -68,22 +71,50 @@ const UserJoinedProgramsCard = () => {
duration: 5000,
isClosable: true,
});
} finally {
setLoading(false);
return [];
}
};
},
enabled: !!user,
refetchOnWindowFocus: false,
});

if (user && user.username) {
fetchJoinedPrograms();
useEffect(() => {
if (joinedProgramsData && !joinedProgramsIsLoading) {
setJoinedPrograms(joinedProgramsData);
setLoading(false);
}
}, [user, sessionToken]);
}, [joinedProgramsData, joinedProgramsIsLoading]);

const handleStartSession = (program, exerciseId) => {
setSelectedExerciseId(exerciseId);
setSelectedProgram(program);
onOpen();
};

const shouldUserRest = (restIntervalOnDays, lastWorkoutDate) => {
if (!lastWorkoutDate) {
return false;
}

const lastWorkoutDateTime = new Date(lastWorkoutDate).getTime();
const timeSinceLastWorkout = Date.now() - lastWorkoutDateTime;
const timeSinceLastWorkoutInDays = timeSinceLastWorkout / (1000 * 60 * 60 * 24);

return timeSinceLastWorkoutInDays < restIntervalOnDays;
};

const remainingRestDays = (restIntervalOnDays, lastWorkoutDate) => {
if (!lastWorkoutDate) {
return 0;
}

const lastWorkoutDateTime = new Date(lastWorkoutDate).getTime();
const timeSinceLastWorkout = Date.now() - lastWorkoutDateTime;
const remainingDays = restIntervalOnDays - (timeSinceLastWorkout / (1000 * 60 * 60 * 24));

return Math.ceil(remainingDays);
};

if (loading) {
return (
<Box display="flex" justifyContent="center" alignItems="center" height="100vh">
Expand Down Expand Up @@ -143,7 +174,9 @@ const UserJoinedProgramsCard = () => {
p={4}
boxShadow="md"
>
<Heading size="md" mb={4} color="gray.700" display="flex" alignItems="center" gap={3}>
<Heading size="md" mb={4} color="gray.700" display="flex" alignItems="flex-start" gap={3}
flexDirection={"column"}
>
{program.title}
<div className="flex space-x-2">
<span className="inline-flex items-center px-2 py-1 text-sm font-medium text-blue-800 bg-blue-100 rounded">
Expand Down Expand Up @@ -174,7 +207,15 @@ const UserJoinedProgramsCard = () => {
<Td>{exercise.exercise.name}</Td>
<Td>
<Button
onClick={() => handleStartSession(program, exercise.id)}
onClick={() => {
if (shouldUserRest(program.interval, program.lastCompletedWorkoutDate)) {
setSelectedProgram(program);
setSelectedExerciseId(exercise.id);
onRestOpen();
} else {
handleStartSession(program, exercise.id);
}
}}
colorScheme="green"
size="sm"
>
Expand All @@ -189,13 +230,23 @@ const UserJoinedProgramsCard = () => {
</Table>

{selectedProgram && selectedExerciseId && (
<>
<Detailed_Ex_Modal
isOpen={isOpen}
onClose={onClose}
data={selectedProgram}
setData={setSelectedProgram}
excersizeID={selectedExerciseId}
/>
<RestModal
isOpen={isRestOpen}
onClose={onRestClose}
interval={remainingRestDays(selectedProgram.interval, selectedProgram.lastCompletedWorkoutDate)}
onContinueWorkout={() => {
handleStartSession(selectedProgram, selectedExerciseId);
}}
/>
</>
)}
</Box>
);
Expand Down

0 comments on commit 827d3ba

Please sign in to comment.