разобралась с функцией загрузки прогресса с сервера

This commit is contained in:
Tatyana 2025-09-04 12:08:54 +03:00
parent cf9803ef6f
commit 2b9c198e44
2 changed files with 1149 additions and 102 deletions

View File

@ -41,12 +41,28 @@ export const Exercise = () => {
// ========== ОСНОВНЫЕ СОСТОЯНИЯ КОМПОНЕНТА ==========
// Состояние для хранения данных упражнения (null = данные еще не загружены)
// Мы говорим TypeScript, что переменная exercise может быть либо объектом типа Exercise, либо null. Это важно, потому что изначально у нас нет данных (например, мы их ещё не загрузили), и состояние пустое — null
// exercise — это переменная/ текущее значение состояния, которое может быть объектом Exercise или null.
// setExercise — функция, с помощью которой можно обновить exercise.
// useState<string>("") — это вызов функции useState с аргументом "" (пустая строка).
// Внутри угловых скобок <string> мы указываем тип состояния (TypeScript).
// В круглых скобках ("") — передаём начальное значение состояния.
const [exercise, setExercise] = useState<Exercise | null>(null)
// useState<Exercise | null>(null) — это вызов функции useState с начальным значением null
// Состояние загрузки (true = идет загрузка, false = загрузка завершена)
const [loading, setLoading] = useState(true)
// если TypeScript может сам вывести тип из переданного начального значения, то указывать тип явно не обязательно
// Состояние ошибки (пустая строка = нет ошибки, текст = описание ошибки)
// С помощью ДЕСТРУКТУРИЗАЦИИ МАССИВА мы присваиваем первый элемент массива переменной error, а второй — функции setError
// Деструктуризация массива — это удобный синтаксис в JavaScript/TypeScript, который позволяет распаковать значения из массива в отдельные переменные
// Когда начальное значение — null или undefined, и TypeScript не может понять, какого типа будет состояние.
const [error, setError] = useState<string>("")
// ========== СОСТОЯНИЯ ТАЙМЕРУ УПРАЖНЕНИЯ ==========
@ -98,6 +114,10 @@ export const Exercise = () => {
const loadProgress = async () => {
const serverLoaded = await loadProgressFromServer()
// async — говорит, что функция работает асинхронно и возвращает промис.
// await — заставляет функцию ждать завершения промиса, не блокируя при этом весь поток выполнения.
if (!serverLoaded) {
// Fallback to localStorage if server loading failed
console.log("Пытаемся загрузить прогресс из localStorage как резервный вариант")
@ -208,90 +228,98 @@ export const Exercise = () => {
setLoading(false)
})
}, [courseId, exerciseId]) // Код выполняется при изменении ID курса или упражнения
// ========== ФУНКЦИЯ ЗАГРУЗКИ ПРОГРЕССА С СЕРВЕРА ==========
// Эта функция получает сохраненный прогресс упражнения с бэкенда
const loadProgressFromServer = async (): Promise<boolean> => {
const loadProgressFromServer = async () => {
try {
console.log("Загружаем сохраненный прогресс с сервера для упражнения:", exerciseId, "в курсе:", courseId)
console.log(`Загружаем прогресс для курса ${courseId} и упражнения ${exerciseId}`);
const response = await connect.get(`pacient/${courseId}/${exerciseId}/progress?course_id=${courseId}`)
const token = localStorage.getItem('authToken');
if (response.data && response.data.length > 0) {
console.log("Найден сохраненный прогресс на сервере для курса", courseId, ":", response.data)
// Выполняем GET-запрос на сервер
const response = await connect.get(`pacient/${courseId}/${exerciseId}`, {
headers: {
Authorization: `Bearer ${token}`,
},
});
// Получаем все записи прогресса для этого упражнения В ЭТОМ КУРСЕ
const progressData = response.data
// Логируем ответ
console.log("Ответ сервера с прогрессом:", response.data);
const filteredProgress = progressData.filter((record: any) => {
// Проверяем что запись относится к текущему курсу
return record.course_id === Number.parseInt(courseId) || record.CourseId === Number.parseInt(courseId)
})
// Проверяем наличие user_progress
if (
response.data &&
response.data.user_progress &&
Array.isArray(response.data.user_progress) &&
response.data.user_progress.length > 0
) {
const progressArray = response.data.user_progress;
console.log("Отфильтрованный прогресс для курса", courseId, ":", filteredProgress)
// Фильтруем прогресс по текущему курсу
const filteredProgress = progressArray.filter((record: any) => {
return record.course_id === Number.parseInt(courseId);
});
// Находим завершенные подходы (status = 1) и незавершенные (status = 0)
const completedSetsFromServer: number[] = []
let lastIncompleteSet = null
let lastSavedTime = "00:00"
console.log("Отфильтрованный прогресс:", filteredProgress);
// Обрабатываем каждую запись прогресса ТОЛЬКО ДЛЯ ТЕКУЩЕГО КУРСА
const completedSetsFromServer: number[] = [];
let lastIncompleteSet: any = null;
let lastSavedTime = "00:00";
// Обрабатываем каждый прогресс
filteredProgress.forEach((record: any) => {
console.log(
`Обрабатываем запись для курса ${courseId}: подход ${record.set}, статус ${record.status}, время ${record.time_users}`,
)
console.log(`Подход ${record.set}, статус ${record.status}, время ${record.time_users}`);
if (record.status === 1) {
// СТАТУС 1 = подход завершен
completedSetsFromServer.push(record.set)
console.log(`Подход ${record.set} завершен`)
// Завершённый подход
completedSetsFromServer.push(record.set);
} else if (record.status === 0) {
// СТАТУС 0 = подход в процессе или на паузе
lastIncompleteSet = record
lastSavedTime = record.time_users || "00:00"
console.log(`Подход ${record.set} в процессе, время: ${lastSavedTime}`)
// Незавершённый подход
if (!lastIncompleteSet || record.set > lastIncompleteSet.set) {
lastIncompleteSet = record;
lastSavedTime = record.time_users || "00:00";
}
})
}
});
// Восстанавливаем состояние на основе данных с сервера
// Восстановление завершённых подходов
if (completedSetsFromServer.length > 0) {
console.log("Восстанавливаем завершенные подходы:", completedSetsFromServer)
setCompletedSets(completedSetsFromServer)
setCompletedSets(completedSetsFromServer);
}
// Если есть незавершенный подход, восстанавливаем его состояние
// Восстановление незавершённого подхода
if (lastIncompleteSet) {
console.log("Восстанавливаем незавершенный подход:", lastIncompleteSet.set)
const timeInSeconds = convertTimeToSeconds(lastSavedTime);
// Конвертируем время из формата MM:SS в секунды
const timeInSeconds = convertTimeToSeconds(lastSavedTime)
setCurrentSet(lastIncompleteSet.set);
setCurrentTime(timeInSeconds);
setHasSavedProgress(true);
setIsCompleted(false);
setCurrentSet(lastIncompleteSet.set) // Устанавливаем текущий подход
setCurrentTime(timeInSeconds) // Восстанавливаем время
setHasSavedProgress(true) // Помечаем что есть сохраненный прогресс
setIsCompleted(false) // Упражнение не завершено
console.log(`Восстановлено состояние: подход ${lastIncompleteSet.set}, время ${timeInSeconds} секунд`)
console.log(`Восстановлен незавершённый подход ${lastIncompleteSet.set} с временем ${timeInSeconds} секунд`);
}
// Проверяем, завершены ли все подходы
// Проверка, завершены ли все подходы
if (completedSetsFromServer.length >= totalSets) {
console.log("ВСЕ ПОДХОДЫ ЗАВЕРШЕНЫ согласно серверу!")
setIsCompleted(true) // Помечаем упражнение как завершенное
setCurrentTime(totalTime) // Устанавливаем время на максимум
setHasSavedProgress(false) // Сбрасываем флаг сохранения
setIsCompleted(true);
setCurrentTime(totalTime);
setHasSavedProgress(false);
console.log("Все подходы завершены");
}
return true // Successfully loaded from server
return true;
} else {
console.log("Прогресс на сервере не найден, начинаем с чистого листа")
return false // No progress found on server
console.log("Прогресс не найден, начинаем с чистого листа");
return false;
}
} catch (error) {
console.error("Ошибка при загрузке прогресса с сервера:", error)
return false // Failed to load from server
console.error("Ошибка при загрузке прогресса с сервера:", error);
return false;
}
}
};
// ========== ФУНКЦИЯ КОНВЕРТАЦИИ ВРЕМЕНИ ИЗ MM:SS В СЕКУНДЫ ==========
// Преобразует время из формата "MM:SS" в секунды (например: "02:30" = 150 секунд)
const convertTimeToSeconds = (timeString: string): number => {
@ -350,7 +378,7 @@ export const Exercise = () => {
console.log(`!!!!Текущее упражнение: ${currentExerciseNum}, следующее: ${nextExerciseId}`)
console.log({courseId}, {nextExerciseId})
console.log({ courseId }, { nextExerciseId })
// Переходим к следующему упражнению в том же курсе
history.push(`${courseId}/${nextExerciseId}`)
}
@ -718,8 +746,7 @@ export const Exercise = () => {
<button
onClick={() => !isCompleted && !isResting && setIsPlaying(!isPlaying)}
disabled={isCompleted || isResting}
className={`w-20 h-20 rounded-full flex items-center justify-center shadow-2xl transition-all duration-300 transform hover:scale-110 ${
isPlaying
className={`w-20 h-20 rounded-full flex items-center justify-center shadow-2xl transition-all duration-300 transform hover:scale-110 ${isPlaying
? "bg-white/20 backdrop-blur-xl border border-white/30"
: "bg-white/30 backdrop-blur-xl border border-white/50"
} ${isCompleted || isResting ? "opacity-50 cursor-not-allowed" : ""}`}
@ -782,8 +809,7 @@ export const Exercise = () => {
<div key={setNumber} className="flex-1 text-center">
{/* Полоска прогресса для подхода */}
<div
className={`h-3 rounded-full transition-all duration-300 ${
isSetCompleted ? "bg-cyan-500" : isCurrent ? "bg-cyan-500" : "bg-gray-200"
className={`h-3 rounded-full transition-all duration-300 ${isSetCompleted ? "bg-cyan-500" : isCurrent ? "bg-cyan-500" : "bg-gray-200"
}`}
/>
{/* Номер подхода под полоской */}
@ -825,8 +851,7 @@ export const Exercise = () => {
<div className="flex items-center space-x-2">
{/* Индикатор активности */}
<div
className={`w-2 h-2 rounded-full ${
isCompleted
className={`w-2 h-2 rounded-full ${isCompleted
? "bg-cyan-400"
: isResting
? `bg-cyan-400 ${!isRestPaused ? "animate-pulse" : ""}`
@ -851,8 +876,7 @@ export const Exercise = () => {
{/* Полоска прогресса */}
<div className="bg-gray-200 rounded-full h-2 mb-4 overflow-hidden">
<div
className={`h-2 rounded-full transition-all duration-1000 shadow-sm ${
isCompleted
className={`h-2 rounded-full transition-all duration-1000 shadow-sm ${isCompleted
? "bg-gradient-to-r from-cyan-400 via-cyan-500 to-cyan-600"
: isResting
? "bg-gradient-to-r from-cyan-400 via-cyan-500 to-cyan-600"
@ -889,8 +913,7 @@ export const Exercise = () => {
// Сохраняем состояние паузы
handlePause(completedSets, currentSet, true, restTime, !isRestPaused)
}}
className={`flex-1 font-bold py-3 px-4 rounded-xl transition-all duration-300 flex items-center justify-center space-x-2 ${
isRestPaused
className={`flex-1 font-bold py-3 px-4 rounded-xl transition-all duration-300 flex items-center justify-center space-x-2 ${isRestPaused
? "bg-yellow-500 hover:bg-yellow-600 text-white"
: "bg-[#2BACBE] hover:bg-[#2099A8] text-white"
}`}
@ -937,8 +960,7 @@ export const Exercise = () => {
setIsPlaying(false)
}
}}
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
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-yellow-500 hover:bg-yellow-600 text-white shadow-lg"
: "bg-[#2BACBE] hover:bg-[#2099A8] text-white shadow-lg"
}`}

1025
src/pages/ExerciseOld.tsx Normal file

File diff suppressed because it is too large Load Diff