From 90267942d7a44aa75e20ff8b2f0a1e4a741e7a31 Mon Sep 17 00:00:00 2001 From: Tatyana Date: Mon, 8 Sep 2025 21:01:13 +0300 Subject: [PATCH] =?UTF-8?q?=D0=B8=D1=81=D0=BF=D1=80=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=B0=20=D0=BD=D0=B8=D0=B6=D0=BD=D1=8F=D1=8F=20?= =?UTF-8?q?=D0=BD=D0=B0=D0=B2=D0=B8=D0=B3=D0=B0=D1=86=D0=B8=D1=8F=20-=20?= =?UTF-8?q?=D0=BF=D0=B5=D1=80=D0=B5=D1=85=D0=BE=D0=B4=20=D0=BD=D0=B0=20?= =?UTF-8?q?=D1=82=D0=B5=D0=BA=D1=83=D1=89=D0=B5=D0=B5=20=D1=83=D0=BF=D1=80?= =?UTF-8?q?=D0=B0=D0=B6=D0=BD=D0=B5=D0=BD=D0=B8=D0=B5/=D1=82=D1=80=D0=B5?= =?UTF-8?q?=D0=BD=D0=B8=D1=80=D0=BE=D0=B2=D0=BA=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/BottomNavigation.tsx | 263 ++++++++++++++++++++++++++-- 1 file changed, 247 insertions(+), 16 deletions(-) diff --git a/src/components/BottomNavigation.tsx b/src/components/BottomNavigation.tsx index c73b5b1..0d20964 100644 --- a/src/components/BottomNavigation.tsx +++ b/src/components/BottomNavigation.tsx @@ -1,32 +1,267 @@ "use client" -import type React from "react"; -import { useHistory, useLocation } from "react-router-dom"; +import type React from "react" +import { useHistory, useLocation } from "react-router-dom" +import { useState, useEffect } from "react" +import { connect } from "../confconnect" -import { getRouteHome } from "../shared/consts/router"; -import { getRouteCourses } from "../shared/consts/router"; -import { getRouteExerciseByIndex } from "../shared/consts/router"; -import { getRouteSettings } from "../shared/consts/router"; +import { getRouteHome } from "../shared/consts/router" +import { getRouteCourses } from "../shared/consts/router" +import { getRouteExerciseByIndex } from "../shared/consts/router" +import { getRouteSettings } from "../shared/consts/router" -import { HomeIcon } from "./icons/HomeIcon"; -import { CalendarIcon } from "./icons/CalendarIcon"; -import { DumbbellIcon } from "./icons/DumbbellIcon"; -import { SettingsIcon } from "./icons/SettingsIcon"; +import { HomeIcon } from "./icons/HomeIcon" +import { CalendarIcon } from "./icons/CalendarIcon" +import { DumbbellIcon } from "./icons/DumbbellIcon" +import { SettingsIcon } from "./icons/SettingsIcon" +import type { Course, User, CoursesApiResponse } from "../types/course" + +interface CourseExercises { + id_exercise: number + day: number + position: number + title: string + desc: string + time: string + count: number + repeats: number +} + +interface CurrentExercise { + courseId: string + exerciseId: string + exerciseIndex: number + day: number + title: string + position: number + isCompleted: boolean +} const BottomNavigation: React.FC = () => { const history = useHistory() const location = useLocation() + const [currentExercise, setCurrentExercise] = useState(null) + const [courses, setCourses] = useState([]) + + const token = localStorage.getItem("authToken") + + const getCurrentExercise = async (): Promise => { + if (!token || courses.length === 0) return null + + try { + let targetCourse: Course | null = null + + console.log("[v0] BottomNav - Всего курсов:", courses.length) + console.log( + "[v0] BottomNav - Список курсов:", + courses.map((c) => ({ id: c.id, title: c.title })), + ) + + // Проходим по всем курсам и ищем незавершенный + for (const course of courses) { + console.log("[v0] BottomNav - Проверяем курс:", course.id, course.title) + + // Загружаем упражнения для каждого курса + const response = await connect.get(`/pacient/${course.id}`, { + headers: { + Authorization: `Bearer ${token}`, + "Content-Type": "application/json", + }, + }) + + const exercises = response.data.course_exercises || [] + console.log("[v0] BottomNav - Упражнений в курсе", course.id, ":", exercises.length) + + if (exercises.length === 0) continue // Пропускаем курсы без упражнений + + // Проверяем, есть ли незавершенные упражнения в этом курсе + let hasIncompleteExercises = false + let completedCount = 0 + + for (const exercise of exercises) { + const storageKey = `exerciseProgress_${course.id}_${exercise.id_exercise}_day_${exercise.day}` + const savedProgress = localStorage.getItem(storageKey) + + let isCompleted = false + if (savedProgress) { + const progress = JSON.parse(savedProgress) + isCompleted = + progress.status === 1 && progress.completedSets && progress.completedSets.length >= exercise.count + } + + if (isCompleted) { + completedCount++ + } else { + hasIncompleteExercises = true + } + } + + console.log( + "[v0] BottomNav - Курс", + course.id, + "- завершено упражнений:", + completedCount, + "из", + exercises.length, + ) + console.log("[v0] BottomNav - Курс", course.id, "- есть незавершенные:", hasIncompleteExercises) + + // Если в курсе есть незавершенные упражнения, выбираем его + if (hasIncompleteExercises) { + targetCourse = course + console.log("[v0] BottomNav - Выбран курс как незавершенный:", course.id, course.title) + break // Берем первый найденный незавершенный курс + } + } + + // Если все курсы завершены, берем последний курс + if (!targetCourse && courses.length > 0) { + targetCourse = courses[courses.length - 1] + console.log("[v0] BottomNav - Все курсы завершены, берем последний:", targetCourse.id, targetCourse.title) + } + + if (!targetCourse) { + console.log("[v0] BottomNav - Не найден подходящий курс") + return null + } + + console.log("[v0] BottomNav - Итоговый выбранный курс:", targetCourse.id, targetCourse.title) + + // Загружаем упражнения выбранного курса + const response = await connect.get(`/pacient/${targetCourse.id}`, { + headers: { + Authorization: `Bearer ${token}`, + "Content-Type": "application/json", + }, + }) + + const exercises = response.data.course_exercises || [] + + if (exercises.length === 0) { + return null + } + + // Группируем упражнения по дням + const exercisesByDay = exercises.reduce((acc: any, exercise: CourseExercises) => { + if (!acc[exercise.day]) { + acc[exercise.day] = [] + } + acc[exercise.day].push(exercise) + return acc + }, {}) + + // Ищем первое незавершенное упражнение + let foundExercise: CurrentExercise | null = null + + for (const day of Object.keys(exercisesByDay).sort((a, b) => Number(a) - Number(b))) { + const dayExercises = exercisesByDay[day].sort( + (a: CourseExercises, b: CourseExercises) => a.position - b.position, + ) + + for (let i = 0; i < dayExercises.length; i++) { + const exercise = dayExercises[i] + + // Проверяем прогресс упражнения в localStorage + const storageKey = `exerciseProgress_${targetCourse.id}_${exercise.id_exercise}_day_${day}` + const savedProgress = localStorage.getItem(storageKey) + + let isCompleted = false + if (savedProgress) { + const progress = JSON.parse(savedProgress) + isCompleted = + progress.status === 1 && progress.completedSets && progress.completedSets.length >= exercise.count + } + + if (!isCompleted) { + foundExercise = { + courseId: targetCourse.id.toString(), + exerciseId: exercise.id_exercise.toString(), + exerciseIndex: i, + day: Number(day), + title: exercise.title, + position: exercise.position, + isCompleted: false, + } + break + } + } + + if (foundExercise) break + } + + // Если все упражнения завершены, берем последнее + if (!foundExercise && exercises.length > 0) { + const lastExercise = exercises[exercises.length - 1] + foundExercise = { + courseId: targetCourse.id.toString(), + exerciseId: lastExercise.id_exercise.toString(), + exerciseIndex: exercises.length - 1, + day: lastExercise.day, + title: lastExercise.title, + position: lastExercise.position, + isCompleted: true, + } + } + + return foundExercise + } catch (error) { + console.error("[v0] BottomNav - Ошибка при получении текущего упражнения:", error) + return null + } + } + + useEffect(() => { + if (!token) return + + connect + .get("/pacient/courses") + .then((response) => { + const users = response.data.courses || [] + const allCourses: Course[] = [] + + users.forEach((user: User) => { + if (user.Courses && Array.isArray(user.Courses)) { + user.Courses.forEach((course) => { + allCourses.push({ + id: course.id, + title: course.title, + desc: course.desc, + url_file_img: course.url_file_img, + course_exercises: course.course_exercises, + }) + }) + } + }) + + setCourses(allCourses) + }) + .catch((error) => { + console.error("[v0] BottomNav - Ошибка при загрузке курсов:", error) + }) + }, [token]) + + useEffect(() => { + if (courses.length > 0) { + getCurrentExercise().then(setCurrentExercise) + } + }, [courses]) + + const getWorkoutPath = () => { + if (currentExercise) { + return getRouteExerciseByIndex(currentExercise.courseId, currentExercise.exerciseIndex, currentExercise.day) + } + return getRouteExerciseByIndex(1, 0, 1) + } const navItems = [ { path: getRouteHome(), icon: HomeIcon, label: "Домой" }, { path: getRouteCourses(), icon: CalendarIcon, label: "Курсы" }, - { path: getRouteExerciseByIndex(1,1,1), icon: DumbbellIcon, label: "Тренировка" }, + { path: getWorkoutPath(), icon: DumbbellIcon, label: "Тренировка" }, { path: getRouteSettings(), icon: SettingsIcon, label: "Меню" }, ] const isActive = (path: string) => { - // Проверка на совпадение или включение return location.pathname === path || location.pathname.startsWith(path) } @@ -42,13 +277,11 @@ const BottomNavigation: React.FC = () => { onClick={() => history.push(item.path)} className="relative flex flex-col items-center justify-center w-24 h-24 overflow-hidden group focus:outline-none focus-visible:ring-1 focus-visible:ring-white focus-visible:ring-offset-2 focus-visible:ring-offset-[#0D212C]" > - {/* Active state background (glassmorphism rectangle) */}
- {/* Icon and Label container */}
{ {item.label} {item.label}
- {/* Bottom circle with glow */}
- {/* Внутренний маленький круг */}