надо решить проблему с роутингом по упражнениям курса / у нас должны учитываться дни, т.к. массив упражнений фильтруется по дням и маршруты неверные получаются

This commit is contained in:
Tatyana 2025-09-04 22:13:37 +03:00
parent 2b9c198e44
commit f9e5d3614a
3 changed files with 27 additions and 63 deletions

View File

@ -15,6 +15,7 @@ import type { Course } from "../pages/Courses";
// import { Exercise } from "./Exercise";
export interface CourseExercises {
id: number;
id_course: number;
id_exercise: number;
exercise: Exercise;
@ -42,6 +43,8 @@ export const CourseExercises = () => {
const [course_exercises, setExercises] = useState<CourseExercises[]>([]);
const [selectedDay, setSelectedDay] = useState<number | null>(null);
const token = localStorage.getItem('authToken');
useEffect(() => {
@ -60,13 +63,10 @@ export const CourseExercises = () => {
})
.then(response => {
console.log('Response status:', response.status);
console.log('Данные страницы упражнения курса:', response.data);
// setPacientData(response.data);
console.log('Данные упражнения курса:', response.data.course_exercises);
setExercises(response.data.course_exercises);
})
.catch(error => {
if (error.response) {
@ -88,27 +88,6 @@ export const CourseExercises = () => {
}
}, [course_exercises]);
//Этот код — это React-хук useEffect, который выполняет определённый блок кода при изменении зависимостей. Давайте разберём его подробно:
//Он следит за изменением массива course_exercises. Когда course_exercises обновляется (например, загружается с сервера или изменяется), он проверяет:
// Есть ли в массиве упражнения (course_exercises.length > 0)
// И выбран ли уже день (selectedDay === null)
// Если оба условия выполняются, то:
// Устанавливает selectedDay в значение day первого элемента массива course_exercises[0].
// Почему это нужно?
// Это логика инициализации:
// Когда данные о упражнениях загружены впервые, и пользователь ещё не выбрал день (selectedDay равен null),
// автоматически выбирается первый день из загруженных упражнений.
// Почему [course_exercises] в конце?
// Это массив зависимостей. Он говорит React:
// Запускать этот эффект только тогда, когда course_exercises изменится.
// Без этого эффекта он бы запускался при каждом рендере, что нежелательно.
const uniqueDays = Array.from(new Set(course_exercises.map(ex => ex.day))).sort((a, b) => a - b);
const dayMap: { [key: number]: number } = {};
@ -125,13 +104,12 @@ export const CourseExercises = () => {
? course_exercises.filter(ex => ex.day === selectedDay)
: course_exercises;
console.log('отфильтрованный список по дням',filteredExercises)
return (
<div className="my-36 min-h-screen max-w-4xl mx-auto">
<HeaderNav item={course?.title ?? 'Название курса'} text={'курс'} />
{/* Это выражение использует оператор опциональной цепочки (?.) и оператор нулевого слияния (??).
Если course не null и не undefined, то взять его свойство title, иначе вернуть undefined*/}
<div className="px-6 mb-8">
{/* Заголовок секции */}
<div className="flex flex-col sm:flex-row justify-between content-center mb-6">
@ -147,10 +125,13 @@ export const CourseExercises = () => {
<div className="mb-6">
<div className="flex flex-wrap gap-2 overflow-x-auto pb-2">
{days.map((day, index) => (
<button
key={day}
onClick={() => {
setSelectedDay(day);
}}
className={`flex-shrink-0 px-4 py-2 rounded-full text-xs sm:text-sm font-semibold transition-all duration-300 inline-block ${selectedDay === day
? "bg-[#2BACBE] text-white shadow-lg"
@ -167,11 +148,21 @@ export const CourseExercises = () => {
<div className="exercise-list mb-20">
{filteredExercises.length > 0 ? (
{
filteredExercises.length > 0 ? (
filteredExercises.map((item, index) => (
<div
key={index}
onClick={() => history.push(getRouteExercise(item.id_course.toString(), item.id_exercise.toString()))}
onClick={() => {
// console.log(course_exercises.map(ex => ex.id_exercise))
// Передаем id_course и индекс из полного массива
history.push(getRouteExercise(item.id_course.toString(), item.id_exercise.toString()));
}}
className="p-4 mb-4 cursor-pointer hover:scale-105 transition duration-300 glass-morphism rounded-3xl border border-white/50 shadow-2xl overflow-hidden backdrop-blur-2xl relative">
<div className="flex justify-between items-center">
<div>

View File

@ -28,7 +28,7 @@ export interface Exercise {
sessionname: string // Название тренировочной сессии
}
// Описываем параметры, которые приходят из URL страницы
interface RouteParams {
courseId: string // ID курса (программы тренировок)
exerciseId: string // ID конкретного упражнения
@ -38,41 +38,14 @@ export const Exercise = () => {
// Получаем функции для навигации и параметры из URL
const history = useHistory()
const { courseId, exerciseId } = useParams<RouteParams>()
// ========== ОСНОВНЫЕ СОСТОЯНИЯ КОМПОНЕНТА ==========
// Состояние для хранения данных упражнения (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>("")
// ========== СОСТОЯНИЯ ТАЙМЕРУ УПРАЖНЕНИЯ ==========
// Играет ли сейчас таймер упражнения (true = идет, false = на паузе)
const [isPlaying, setIsPlaying] = useState(false)
// Текущее время выполнения упражнения в секундах (0 = начало)
const [currentTime, setCurrentTime] = useState(0)
// Общее время упражнения в секундах (по умолчанию 15 минут = 900 секунд)
const [totalTime, setTotalTime] = useState(900)
// ========== СОСТОЯНИЯ ПОДХОДОВ (СЕТОВ) ==========

View File

@ -4,7 +4,7 @@ export const getRouteHome = () => `/home`
export const getRouteForgotPassword = () => `/forgot-password`
export const getRouteCourses = () => `/courses`
export const getRouteCourseExercises = (id: number | string) => `/course/${id}`
export const getRouteExercise = (courseId: number | string, exerciseId: number | string) =>
`/course/${courseId}/${exerciseId}`
export const getRouteExercise = (courseId: number | string, day: number | string, exerciseId: number | string) =>
`/course/${courseId}/${day}/${exerciseId}`
export const getRouteSettings = () => `/settings`
export const getRouteCourseComplete = () => `/course-complete`