надо решить проблему с роутингом по упражнениям курса / у нас должны учитываться дни, т.к. массив упражнений фильтруется по дням и маршруты неверные получаются
This commit is contained in:
parent
2b9c198e44
commit
f9e5d3614a
@ -15,6 +15,7 @@ import type { Course } from "../pages/Courses";
|
|||||||
// import { Exercise } from "./Exercise";
|
// import { Exercise } from "./Exercise";
|
||||||
|
|
||||||
export interface CourseExercises {
|
export interface CourseExercises {
|
||||||
|
id: number;
|
||||||
id_course: number;
|
id_course: number;
|
||||||
id_exercise: number;
|
id_exercise: number;
|
||||||
exercise: Exercise;
|
exercise: Exercise;
|
||||||
@ -37,11 +38,13 @@ export const CourseExercises = () => {
|
|||||||
|
|
||||||
const location = useLocation<{ course?: Course }>();
|
const location = useLocation<{ course?: Course }>();
|
||||||
const course = location.state?.course;
|
const course = location.state?.course;
|
||||||
|
|
||||||
|
|
||||||
const [course_exercises, setExercises] = useState<CourseExercises[]>([]);
|
const [course_exercises, setExercises] = useState<CourseExercises[]>([]);
|
||||||
const [selectedDay, setSelectedDay] = useState<number | null>(null);
|
const [selectedDay, setSelectedDay] = useState<number | null>(null);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const token = localStorage.getItem('authToken');
|
const token = localStorage.getItem('authToken');
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -60,13 +63,10 @@ export const CourseExercises = () => {
|
|||||||
|
|
||||||
})
|
})
|
||||||
.then(response => {
|
.then(response => {
|
||||||
console.log('Response status:', response.status);
|
console.log('Данные упражнения курса:', response.data.course_exercises);
|
||||||
console.log('Данные страницы упражнения курса:', response.data);
|
|
||||||
// setPacientData(response.data);
|
|
||||||
setExercises(response.data.course_exercises);
|
setExercises(response.data.course_exercises);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
if (error.response) {
|
if (error.response) {
|
||||||
@ -88,27 +88,6 @@ export const CourseExercises = () => {
|
|||||||
}
|
}
|
||||||
}, [course_exercises]);
|
}, [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 uniqueDays = Array.from(new Set(course_exercises.map(ex => ex.day))).sort((a, b) => a - b);
|
||||||
|
|
||||||
const dayMap: { [key: number]: number } = {};
|
const dayMap: { [key: number]: number } = {};
|
||||||
@ -125,13 +104,12 @@ export const CourseExercises = () => {
|
|||||||
? course_exercises.filter(ex => ex.day === selectedDay)
|
? course_exercises.filter(ex => ex.day === selectedDay)
|
||||||
: course_exercises;
|
: course_exercises;
|
||||||
|
|
||||||
|
console.log('отфильтрованный список по дням',filteredExercises)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="my-36 min-h-screen max-w-4xl mx-auto">
|
<div className="my-36 min-h-screen max-w-4xl mx-auto">
|
||||||
<HeaderNav item={course?.title ?? 'Название курса'} text={'курс'} />
|
<HeaderNav item={course?.title ?? 'Название курса'} text={'курс'} />
|
||||||
{/* Это выражение использует оператор опциональной цепочки (?.) и оператор нулевого слияния (??).
|
|
||||||
Если course не null и не undefined, то взять его свойство title, иначе — вернуть undefined*/}
|
|
||||||
<div className="px-6 mb-8">
|
<div className="px-6 mb-8">
|
||||||
{/* Заголовок секции */}
|
{/* Заголовок секции */}
|
||||||
<div className="flex flex-col sm:flex-row justify-between content-center mb-6">
|
<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="mb-6">
|
||||||
<div className="flex flex-wrap gap-2 overflow-x-auto pb-2">
|
<div className="flex flex-wrap gap-2 overflow-x-auto pb-2">
|
||||||
{days.map((day, index) => (
|
{days.map((day, index) => (
|
||||||
|
|
||||||
|
|
||||||
<button
|
<button
|
||||||
key={day}
|
key={day}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setSelectedDay(day);
|
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
|
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"
|
? "bg-[#2BACBE] text-white shadow-lg"
|
||||||
@ -167,11 +148,21 @@ export const CourseExercises = () => {
|
|||||||
|
|
||||||
|
|
||||||
<div className="exercise-list mb-20">
|
<div className="exercise-list mb-20">
|
||||||
{filteredExercises.length > 0 ? (
|
{
|
||||||
|
|
||||||
|
|
||||||
|
filteredExercises.length > 0 ? (
|
||||||
filteredExercises.map((item, index) => (
|
filteredExercises.map((item, index) => (
|
||||||
<div
|
<div
|
||||||
key={index}
|
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">
|
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 className="flex justify-between items-center">
|
||||||
<div>
|
<div>
|
||||||
|
@ -28,7 +28,7 @@ export interface Exercise {
|
|||||||
sessionname: string // Название тренировочной сессии
|
sessionname: string // Название тренировочной сессии
|
||||||
}
|
}
|
||||||
|
|
||||||
// Описываем параметры, которые приходят из URL страницы
|
|
||||||
interface RouteParams {
|
interface RouteParams {
|
||||||
courseId: string // ID курса (программы тренировок)
|
courseId: string // ID курса (программы тренировок)
|
||||||
exerciseId: string // ID конкретного упражнения
|
exerciseId: string // ID конкретного упражнения
|
||||||
@ -38,41 +38,14 @@ export const Exercise = () => {
|
|||||||
// Получаем функции для навигации и параметры из URL
|
// Получаем функции для навигации и параметры из URL
|
||||||
const history = useHistory()
|
const history = useHistory()
|
||||||
const { courseId, exerciseId } = useParams<RouteParams>()
|
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)
|
const [exercise, setExercise] = useState<Exercise | null>(null)
|
||||||
// useState<Exercise | null>(null) — это вызов функции useState с начальным значением null
|
|
||||||
|
|
||||||
// Состояние загрузки (true = идет загрузка, false = загрузка завершена)
|
|
||||||
const [loading, setLoading] = useState(true)
|
const [loading, setLoading] = useState(true)
|
||||||
// если TypeScript может сам вывести тип из переданного начального значения, то указывать тип явно не обязательно
|
|
||||||
|
|
||||||
// Состояние ошибки (пустая строка = нет ошибки, текст = описание ошибки)
|
|
||||||
// С помощью ДЕСТРУКТУРИЗАЦИИ МАССИВА мы присваиваем первый элемент массива переменной error, а второй — функции setError
|
|
||||||
// Деструктуризация массива — это удобный синтаксис в JavaScript/TypeScript, который позволяет распаковать значения из массива в отдельные переменные
|
|
||||||
// Когда начальное значение — null или undefined, и TypeScript не может понять, какого типа будет состояние.
|
|
||||||
const [error, setError] = useState<string>("")
|
const [error, setError] = useState<string>("")
|
||||||
|
|
||||||
// ========== СОСТОЯНИЯ ТАЙМЕРУ УПРАЖНЕНИЯ ==========
|
// ========== СОСТОЯНИЯ ТАЙМЕРУ УПРАЖНЕНИЯ ==========
|
||||||
// Играет ли сейчас таймер упражнения (true = идет, false = на паузе)
|
|
||||||
const [isPlaying, setIsPlaying] = useState(false)
|
const [isPlaying, setIsPlaying] = useState(false)
|
||||||
|
|
||||||
// Текущее время выполнения упражнения в секундах (0 = начало)
|
|
||||||
const [currentTime, setCurrentTime] = useState(0)
|
const [currentTime, setCurrentTime] = useState(0)
|
||||||
|
|
||||||
// Общее время упражнения в секундах (по умолчанию 15 минут = 900 секунд)
|
|
||||||
const [totalTime, setTotalTime] = useState(900)
|
const [totalTime, setTotalTime] = useState(900)
|
||||||
|
|
||||||
// ========== СОСТОЯНИЯ ПОДХОДОВ (СЕТОВ) ==========
|
// ========== СОСТОЯНИЯ ПОДХОДОВ (СЕТОВ) ==========
|
||||||
|
@ -4,7 +4,7 @@ export const getRouteHome = () => `/home`
|
|||||||
export const getRouteForgotPassword = () => `/forgot-password`
|
export const getRouteForgotPassword = () => `/forgot-password`
|
||||||
export const getRouteCourses = () => `/courses`
|
export const getRouteCourses = () => `/courses`
|
||||||
export const getRouteCourseExercises = (id: number | string) => `/course/${id}`
|
export const getRouteCourseExercises = (id: number | string) => `/course/${id}`
|
||||||
export const getRouteExercise = (courseId: number | string, exerciseId: number | string) =>
|
export const getRouteExercise = (courseId: number | string, day: number | string, exerciseId: number | string) =>
|
||||||
`/course/${courseId}/${exerciseId}`
|
`/course/${courseId}/${day}/${exerciseId}`
|
||||||
export const getRouteSettings = () => `/settings`
|
export const getRouteSettings = () => `/settings`
|
||||||
export const getRouteCourseComplete = () => `/course-complete`
|
export const getRouteCourseComplete = () => `/course-complete`
|
||||||
|
Loading…
x
Reference in New Issue
Block a user