diff --git a/src/pages/Exercise.tsx b/src/pages/Exercise.tsx index ca8d852..1e84631 100644 --- a/src/pages/Exercise.tsx +++ b/src/pages/Exercise.tsx @@ -1,22 +1,14 @@ "use client" -//хуки -import { useState, useEffect } from "react"; -import { useHistory, useParams } from "react-router-dom"; +import { useState, useEffect } from "react" +import { useHistory, useParams } from "react-router-dom" -// import { CheckIcon } from "../components/icons/CheckIcon"; -import { RefreshIcon } from "../components/icons/RefreshIcon"; +import { RefreshIcon } from "../components/icons/RefreshIcon" -import HeaderNav from "../components/HeaderNav"; -import BottomNavigation from "../components/BottomNavigation"; - -// import { getRouteCourseComplete } from "../shared/consts/router"; -import { connect } from "../confconnect"; -import axios from "axios" - - -//В TypeScript ключевое слово interface используется для определения интерфейсов — это способ описать структуру объектов, то есть какие свойства и методы у них есть, и какие типы данных они содержат +import HeaderNav from "../components/HeaderNav" +import BottomNavigation from "../components/BottomNavigation" +import { connect } from "../confconnect" export interface Exercise { id: number @@ -45,30 +37,43 @@ export const Exercise = () => { const [error, setError] = useState("") const [isPlaying, setIsPlaying] = useState(false) const [currentTime, setCurrentTime] = useState(0) - const [totalTime, setTotalTime] = useState(900) // Default 15 minutes + const [totalTime, setTotalTime] = useState(900) const [currentSet, setCurrentSet] = useState(1) - const [isActive, setIsActive] = useState(false) const [totalSets, setTotalSets] = useState(3) const [isRotating, setIsRotating] = useState(false) + const [hasSavedProgress, setHasSavedProgress] = useState(false) + const [isCompleted, setIsCompleted] = useState(false) + const [completedSets, setCompletedSets] = useState([]) - - - //Можно реализовать полностью на клиенте. - // Ограничение — прогресс сохраняется только на устройстве пользователя. - // Если нужно синхронизировать между устройствами или сохранять историю, потребуется бэкенд. - //сохраняем прогресс + // Новые состояния для отдыха + const [isResting, setIsResting] = useState(false) + const [restTime, setRestTime] = useState(0) + const [totalRestTime] = useState(60) // 60 секунд отдыха по умолчанию // Восстановление прогресса при загрузке useEffect(() => { - const savedProgress = localStorage.getItem('exerciseProgress'); + const savedProgress = localStorage.getItem("exerciseProgress") if (savedProgress) { - const progress = JSON.parse(savedProgress); + const progress = JSON.parse(savedProgress) if (progress.exerciseId === exercise?.id) { - setCurrentTime(progress.position); - setCurrentSet(progress.set); + if (progress.status === 1) { + setIsCompleted(true) + setCurrentTime(progress.totalTime || totalTime) + setCompletedSets(progress.completedSets || []) + setCurrentSet(progress.set || totalSets) + setHasSavedProgress(false) + } else { + setCurrentTime(progress.position) + setCurrentSet(progress.set) + setCompletedSets(progress.completedSets || []) + setIsResting(progress.isResting || false) + setRestTime(progress.restTime || 0) + setHasSavedProgress(true) + setIsCompleted(false) + } } } - }, [exercise]); + }, [exercise, totalTime, totalSets]) useEffect(() => { console.log("Course ID:", courseId) @@ -80,13 +85,9 @@ export const Exercise = () => { return } - // Получаем данные упражнения через API: GET /pacient/:course_id/:exercise_id connect .get(`pacient/${courseId}/${exerciseId}`) .then((response) => { - // console.log("Response status:", response.status) - // console.log("Response data:", response.data) - const exerciseData = response.data setExercise({ @@ -103,9 +104,10 @@ export const Exercise = () => { sessionname: exerciseData.sessionname, }) - // Устанавливаем время и подходы из данных упражнения if (exerciseData.time) { - setTotalTime(Number.parseInt(exerciseData.time) * 60) // Конвертируем минуты в секунды + const timeInSeconds = Number.parseInt(exerciseData.time) * 60 + setTotalTime(timeInSeconds) + console.log("Установлено время упражнения:", timeInSeconds, "секунд") } if (exerciseData.count) { setTotalSets(exerciseData.count) @@ -131,30 +133,104 @@ export const Exercise = () => { }) }, [courseId, exerciseId]) - - - -const handlePause = async () => { - // Формируем данные в том формате, который ожидает сервер - const progressData = { - time_users: formatTime(currentTime), // Отправляем как строку в формате MM:SS - status: currentSet, // Отправляем текущий подход как статус + // Функция для завершения текущего подхода + const handleCompleteSet = async () => { + console.log("Завершение подхода", currentSet, "из", totalSets) + + const newCompletedSets = [...completedSets, currentSet] + setCompletedSets(newCompletedSets) + setIsPlaying(false) + + // Проверяем, завершены ли все подходы + if (newCompletedSets.length >= totalSets) { + console.log("Все подходы завершены, завершаем упражнение") + // Все подходы завершены - завершаем упражнение + await handleComplete(newCompletedSets) + } else { + console.log("Начинаем отдых перед подходом", currentSet + 1) + // Начинаем отдых перед следующим подходом + setIsResting(true) + setRestTime(totalRestTime) + setCurrentTime(0) + + // Сохраняем прогресс с отдыхом + await handlePause(newCompletedSets, currentSet, true, totalRestTime) + } + } + + // Функция для завершения упражнения + const handleComplete = async (completedSetsArray = completedSets) => { + console.log("Завершение упражнения") + + const completionData = { + time_users: formatTime(totalTime), + status: 1, + } + + localStorage.setItem( + "exerciseProgress", + JSON.stringify({ + exerciseId: exercise?.id, + position: totalTime, + set: totalSets, + status: 1, + totalTime: totalTime, + completedSets: completedSetsArray, + completedAt: new Date().toISOString(), + }), + ) + + setIsCompleted(true) + setCurrentTime(totalTime) + setIsPlaying(false) + setIsResting(false) + setHasSavedProgress(false) + setCompletedSets(completedSetsArray) + + try { + console.log("Отправляем данные о завершении:", completionData) + + const response = await connect.post(`pacient/${courseId}/${exerciseId}`, completionData) + + console.log("Ответ сервера при завершении:", response.status) + console.log("Упражнение успешно завершено") + } catch (error) { + console.error("Ошибка при отправке завершения:", error) + if (error.response) { + console.error("Ответ сервера:", error.response.status, error.response.data) + } + } + } + + const handlePause = async ( + completedSetsArray = completedSets, + setNumber = currentSet, + resting = isResting, + currentRestTime = restTime, + ) => { + const progressData = { + time_users: formatTime(currentTime), + status: setNumber, } - // Сохраняем в localStorage localStorage.setItem( "exerciseProgress", JSON.stringify({ exerciseId: exercise?.id, position: currentTime, - set: currentSet, + set: setNumber, + status: 0, + completedSets: completedSetsArray, + isResting: resting, + restTime: currentRestTime, }), ) + setHasSavedProgress(true) + try { console.log("Отправляем данные:", progressData) - // Используем connect (axios) вместо прямого axios const response = await connect.post(`pacient/${courseId}/${exerciseId}`, progressData) console.log("Ответ сервера при отправке прогресса:", response.status) @@ -167,38 +243,67 @@ const handlePause = async () => { } } - + // Основной таймер для упражнения useEffect(() => { let interval: NodeJS.Timeout | undefined - if (isPlaying) { + if (isPlaying && !isCompleted && !isResting) { interval = setInterval(() => { setCurrentTime((prev) => { - if (prev >= totalTime) { + const newTime = prev + 1 + console.log(`Таймер: ${newTime}/${totalTime} секунд`) + + // Проверяем, достигли ли мы конца времени для текущего подхода + if (newTime >= totalTime) { + console.log("Время подхода истекло, завершаем подход") setIsPlaying(false) - // Отправляем результат на сервер при завершении - // submitProgress() - // Show completion animation - // history.push(getRouteCourseComplete()) + // Используем setTimeout чтобы избежать проблем с состоянием в useEffect + setTimeout(() => { + handleCompleteSet() + }, 100) return totalTime } - return prev + 1 + return newTime }) }, 1000) } return () => { if (interval) clearInterval(interval) } - }, [isPlaying, totalTime, history]) + }, [isPlaying, totalTime, isCompleted, isResting, currentSet, completedSets]) + // Таймер для отдыха + useEffect(() => { + let restInterval: NodeJS.Timeout | undefined + if (isResting && restTime > 0) { + restInterval = setInterval(() => { + setRestTime((prev) => { + const newRestTime = prev - 1 + console.log(`Отдых: ${newRestTime} секунд осталось`) + + if (newRestTime <= 0) { + console.log("Отдых закончен, переходим к следующему подходу") + // Отдых закончен, переходим к следующему подходу + setIsResting(false) + setCurrentSet(currentSet + 1) + setCurrentTime(0) + return 0 + } + return newRestTime + }) + }, 1000) + } + return () => { + if (restInterval) clearInterval(restInterval) + } + }, [isResting, restTime, currentSet]) const handleClick = () => { - setIsRotating(true); + setIsRotating(true) } + const handleTransitionEnd = () => { - setIsRotating(false); - }; - - + setIsRotating(false) + } const formatTime = (seconds: number) => { const mins = Math.floor(seconds / 60) @@ -206,7 +311,7 @@ const handlePause = async () => { return `${mins.toString().padStart(2, "0")}:${secs.toString().padStart(2, "0")}` } - const progress = (currentTime / totalTime) * 100 + const progress = isResting ? ((totalRestTime - restTime) / totalRestTime) * 100 : (currentTime / totalTime) * 100 const PlayIcon = () => ( @@ -220,9 +325,18 @@ const handlePause = async () => { ) + const CheckIcon = () => ( + + + + ) + const RestIcon = () => ( + + + + ) - // Генерируем инструкции на основе реальных данных упражнения const exerciseSteps = [ { title: "Описание упражнения", @@ -304,28 +418,72 @@ const handlePause = async () => { return (
- + + +

Если упражнение меньше минуты, то скидывает сразу на отдых

- {exercise.title} + {exercise.title} { + const target = e.target as HTMLImageElement + target.src = "/placeholder.svg?height=300&width=400&text=Упражнение" + }} + />
+
- + {isCompleted ? ( +
+ +
+ ) : isResting ? ( +
+ + + + + +
+ ) : ( + + )}
- {isPlaying && ( + {/* Статус выполнения */} + {isCompleted && ( +
+
+ + Выполнено + +
+ )} + + {isResting && ( +
+
+ + Отдых + +
+ )} + + {isPlaying && !isCompleted && !isResting && (
@@ -335,12 +493,43 @@ const handlePause = async () => { )}
- {formatTime(currentTime)} + + {isResting ? formatTime(restTime) : formatTime(currentTime)} +
+ {/* Индикатор завершенных подходов */} +
+
+

Прогресс подходов

+
+ {Array.from({ length: totalSets }, (_, index) => { + const setNumber = index + 1 + const isSetCompleted = completedSets.includes(setNumber) + const isCurrent = setNumber === currentSet && !isSetCompleted + + return ( +
+
+
{setNumber}
+
+ ) + })} +
+
+ Завершено: {completedSets.length} + Всего: {totalSets} +
+
+
+
{exerciseSteps.map((step, index) => (
{
{/* Fixed Timer at Bottom */} -
+
-
+
- Подход {currentSet} из {totalSets} + {isCompleted + ? `Завершено` + : isResting + ? `Отдых перед подходом ${currentSet + 1}` + : `Подход ${currentSet} из ${totalSets}`}
- {formatTime(currentTime)} / {formatTime(totalTime)} + {isResting ? `${formatTime(restTime)} отдых` : `${formatTime(currentTime)} / ${formatTime(totalTime)}`}
-
+
- + + + + )} - className={`flex-1 font-bold py-3 px-4 rounded-xl transition-all duration-300 transform hover:scale-105 flex items-center justify-center space-x-2 cursor-pointer ${isPlaying - ? "bg-gray-400 text-white shadow-lg" - : "bg-orange-400 text-white shadow-lg" - }`} - > - {isPlaying ? ( - <> - Пауза - - ) : ( - <> - Начать - - )} - - -
diff --git a/src/pages/Home.tsx b/src/pages/Home.tsx index 86fb00a..1abf7b6 100644 --- a/src/pages/Home.tsx +++ b/src/pages/Home.tsx @@ -27,12 +27,15 @@ export default function Home() { const history = useHistory(); const [currentDate, setCurrentDate] = useState(""); - const [error, setError] = useState(''); + const [, setError] = useState(''); const [courses, setCourses] = useState([]); const [loading, setLoading] = useState(true); const token = localStorage.getItem('authToken'); + const exerciseProgress = localStorage.getItem('exerciseProgress'); + const currentProgress = localStorage.getItem('currentProgress') + useEffect(() => { console.log(token) @@ -148,6 +151,8 @@ export default function Home() {
+ +
@@ -171,8 +176,10 @@ export default function Home() {
- {/* Current Exercise */} - + {/* Текущее упражнение */} + + {/* Quick Stats (Total Exercises & Total Courses) */}
@@ -191,6 +198,27 @@ export default function Home() { onClick={handleExercisesClick} />
+ +
+

Взяли из локального хранилища:

+ надо считать прогресс исходя из выполненных/ вопрос: то есть, если выполнено, то как его исключить / как флаг проставлять?== + все курсы мы вяли из ручки курсов, назначенные по роутингу (пути в router.ts) + + надо достать текущий курс. который не выполнен. + + как узнать, что курс выполнен?== + + мы берем данные для текущей тренировки, которые записываются с помощью post, --оттуда достаем данные для текущего упражнения, которое записано (1 строка в БД всегда), + а потом переходим на это упражнение. + + и еще - мы считаем по индексу отображение, это же не повлияет на вывод итоговый на домашней странице, например? + + +
+

exerciseProgress:{exerciseProgress}

+

currentProgress{currentProgress}

+
+