исправления по дизайну
This commit is contained in:
parent
c756041321
commit
26c2d5431e
14
src/App.tsx
14
src/App.tsx
@ -5,7 +5,7 @@ import AppRoutes from "./AppRoutes"
|
|||||||
import "./index.css"
|
import "./index.css"
|
||||||
import "@ionic/react/css/core.css"
|
import "@ionic/react/css/core.css"
|
||||||
|
|
||||||
import {Toaster } from "./components/Sonner"
|
import { Toaster } from "./components/Sonner"
|
||||||
// import Header from "./components/Header"
|
// import Header from "./components/Header"
|
||||||
// import Footer from "./components/Footer"
|
// import Footer from "./components/Footer"
|
||||||
|
|
||||||
@ -13,14 +13,14 @@ const App: React.FC = () => (
|
|||||||
<IonApp>
|
<IonApp>
|
||||||
<IonReactRouter>
|
<IonReactRouter>
|
||||||
|
|
||||||
<IonRouterOutlet>
|
<IonRouterOutlet>
|
||||||
<div className="max-h-screen h-full overflow-auto">
|
<div className="max-h-screen h-full overflow-auto">
|
||||||
|
|
||||||
<AppRoutes />
|
<AppRoutes />
|
||||||
<Toaster />
|
<Toaster />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</IonRouterOutlet>
|
</IonRouterOutlet>
|
||||||
|
|
||||||
</IonReactRouter>
|
</IonReactRouter>
|
||||||
</IonApp>
|
</IonApp>
|
||||||
|
@ -1,23 +1,23 @@
|
|||||||
import { Switch, Route, Redirect } from "react-router-dom"
|
import { Switch, Route, Redirect } from "react-router-dom"
|
||||||
|
|
||||||
import Welcome from "./pages/Welcome"
|
import Welcome from "./pages/Welcome";
|
||||||
import Login from "./pages/Login"
|
import Login from "./pages/Login";
|
||||||
import Home from "./pages/Home"
|
import Home from "./pages/Home";
|
||||||
import ForgotPasword from "./pages/ForgotPassword"
|
import ForgotPasword from "./pages/ForgotPassword";
|
||||||
import { Courses } from "./pages/Courses"
|
import { Courses } from "./pages/Courses";
|
||||||
import { CourseExercises } from "./pages/CourseExercises"
|
import { CourseExercises } from "./pages/CourseExercises";
|
||||||
import { Exercise } from "./pages/Exercise"
|
import { Exercise } from "./pages/Exercise";
|
||||||
|
|
||||||
import Settings from "./pages/Settings"
|
import Settings from "./pages/Settings";
|
||||||
import CourseComplete from "./pages/CourseComplete"
|
import CourseComplete from "./pages/CourseComplete";
|
||||||
|
|
||||||
import { getRouteWelcome } from "./shared/consts/router"
|
import { getRouteWelcome } from "./shared/consts/router";
|
||||||
import { getRouteLogin } from "./shared/consts/router"
|
import { getRouteLogin } from "./shared/consts/router";
|
||||||
import { getRouteHome } from "./shared/consts/router"
|
import { getRouteHome } from "./shared/consts/router";
|
||||||
import { getRouteForgotPassword } from "./shared/consts/router"
|
import { getRouteForgotPassword } from "./shared/consts/router";
|
||||||
import { getRouteCourses } from "./shared/consts/router"
|
import { getRouteCourses } from "./shared/consts/router";
|
||||||
import { getRouteSettings } from "./shared/consts/router"
|
import { getRouteSettings } from "./shared/consts/router";
|
||||||
import { getRouteCourseComplete } from "./shared/consts/router"
|
import { getRouteCourseComplete } from "./shared/consts/router";
|
||||||
|
|
||||||
const AppRoutes = () => (
|
const AppRoutes = () => (
|
||||||
<Switch>
|
<Switch>
|
||||||
|
@ -1,17 +1,17 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import type React from "react"
|
import type React from "react";
|
||||||
import { useHistory, useLocation } from "react-router-dom"
|
import { useHistory, useLocation } from "react-router-dom";
|
||||||
|
|
||||||
import { getRouteHome } from "../shared/consts/router"
|
import { getRouteHome } from "../shared/consts/router";
|
||||||
import { getRouteCourses } from "../shared/consts/router"
|
import { getRouteCourses } from "../shared/consts/router";
|
||||||
import { getRouteCourseExercises } from "../shared/consts/router"
|
import { getRouteCourseExercises } from "../shared/consts/router";
|
||||||
import { getRouteSettings } from "../shared/consts/router"
|
import { getRouteSettings } from "../shared/consts/router";
|
||||||
|
|
||||||
import { HomeIcon } from "./icons/HomeIcon"
|
import { HomeIcon } from "./icons/HomeIcon";
|
||||||
import { CalendarIcon } from "./icons/CalendarIcon"
|
import { CalendarIcon } from "./icons/CalendarIcon";
|
||||||
import { DumbbellIcon } from "./icons/DumbbellIcon"
|
import { DumbbellIcon } from "./icons/DumbbellIcon";
|
||||||
import { SettingsIcon } from "./icons/SettingsIcon"
|
import { SettingsIcon } from "./icons/SettingsIcon";
|
||||||
|
|
||||||
|
|
||||||
const BottomNavigation: React.FC = () => {
|
const BottomNavigation: React.FC = () => {
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import type React from "react"
|
import type React from "react";
|
||||||
import { useHistory } from "react-router-dom"
|
import { useHistory } from "react-router-dom";
|
||||||
import { getRouteCourseExercises } from "../shared/consts/router"
|
import { getRouteCourseExercises } from "../shared/consts/router";
|
||||||
|
|
||||||
interface CourseCardProps {
|
interface CourseCardProps {
|
||||||
id: number
|
id: number
|
||||||
|
@ -1,4 +1,9 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
//Когда нужен "use client"?
|
||||||
|
// Если компонент использует React хуки (useState, useEffect, useContext и т.п.).
|
||||||
|
// Если он взаимодействует с браузерными API или DOM.
|
||||||
|
// Если он содержит обработчики событий или управляет состоянием.
|
||||||
|
|
||||||
|
|
||||||
import type React from "react"
|
import type React from "react"
|
||||||
|
|
||||||
@ -10,6 +15,20 @@ interface StatCardProps {
|
|||||||
onClick: () => void
|
onClick: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Это тип компонента React, который принимает определенные пропсы.
|
||||||
|
// React.ComponentType<...> — это тип компонента, который можно использовать как JSX <Icon />.
|
||||||
|
// Внутри <...> указаны пропсы, которые этот компонент принимает.
|
||||||
|
|
||||||
|
// Какие пропсы у иконки?
|
||||||
|
|
||||||
|
// size?: number — необязательный размер иконки (например, в пикселях).
|
||||||
|
// fill?: string — необязательный цвет заливки.
|
||||||
|
// className?: string — необязственный CSS-класс.
|
||||||
|
// style?: React.CSSProperties — необязательные inline-стили.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export const StatCardHome: React.FC<StatCardProps> = ({
|
export const StatCardHome: React.FC<StatCardProps> = ({
|
||||||
title,
|
title,
|
||||||
subtitle,
|
subtitle,
|
||||||
@ -33,8 +52,7 @@ export const StatCardHome: React.FC<StatCardProps> = ({
|
|||||||
<div className="w-20 h-20 bg-white/20 rounded-2xl flex items-center justify-center shadow-xl mb-4 z-20 relative">
|
<div className="w-20 h-20 bg-white/20 rounded-2xl flex items-center justify-center shadow-xl mb-4 z-20 relative">
|
||||||
<Icon size={40}
|
<Icon size={40}
|
||||||
fill={fill}
|
fill={fill}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Текст */}
|
{/* Текст */}
|
||||||
|
@ -7,6 +7,8 @@ interface WorkoutCardProps {
|
|||||||
onCardClick: () => void
|
onCardClick: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Это означает, что компонент ожидает получить пропс onBackClick, который должен быть функцией, вызываемой при определенном событии (например, при нажатии кнопки "Назад").
|
||||||
|
|
||||||
export const WorkoutCardHome: React.FC<WorkoutCardProps> = ({ onBackClick, onCardClick }) => {
|
export const WorkoutCardHome: React.FC<WorkoutCardProps> = ({ onBackClick, onCardClick }) => {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import type React from "react"
|
import type React from "react";
|
||||||
import { useEffect, useState } from "react"
|
import { useEffect, useState } from "react";
|
||||||
import { useHistory } from "react-router-dom"
|
import { useHistory } from "react-router-dom";
|
||||||
|
|
||||||
import confetti from "../assets/img/Confetti.gif"
|
import confetti from "../assets/img/Confetti.gif";
|
||||||
|
|
||||||
import { getRouteCourses } from "../shared/consts/router"
|
import { getRouteCourses } from "../shared/consts/router";
|
||||||
import { getRouteHome } from "../shared/consts/router"
|
import { getRouteHome } from "../shared/consts/router";
|
||||||
|
|
||||||
const CourseComplete: React.FC = () => {
|
const CourseComplete: React.FC = () => {
|
||||||
const history = useHistory()
|
const history = useHistory()
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import { useState, useEffect } from "react"
|
import { useState, useEffect } from "react";
|
||||||
import { useParams, useHistory } from "react-router-dom"
|
import { useParams, useHistory } from "react-router-dom";
|
||||||
|
|
||||||
import HeaderNav from "../components/HeaderNav"
|
import HeaderNav from "../components/HeaderNav";
|
||||||
import BottomNavigation from "../components/BottomNavigation"
|
import BottomNavigation from "../components/BottomNavigation";
|
||||||
|
|
||||||
import { connect } from '../confconnect';
|
import { connect } from '../confconnect';
|
||||||
|
|
||||||
import { getRouteExercise } from "../shared/consts/router"
|
import { getRouteExercise } from "../shared/consts/router";
|
||||||
|
|
||||||
|
|
||||||
interface CourseExercises {
|
interface CourseExercises {
|
||||||
@ -28,7 +28,7 @@ interface Exercise {
|
|||||||
|
|
||||||
|
|
||||||
export const CourseExercises = () => {
|
export const CourseExercises = () => {
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
const { id } = useParams<{ id: string }>();
|
const { id } = useParams<{ id: string }>();
|
||||||
|
|
||||||
// const [pacientData, setPacientData] = useState(null);
|
// const [pacientData, setPacientData] = useState(null);
|
||||||
@ -36,8 +36,6 @@ const history = useHistory();
|
|||||||
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(() => {
|
||||||
@ -130,26 +128,33 @@ const history = useHistory();
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* надо делать по индексу, чтоб шло по порядку
|
||||||
|
{filteredExercises
|
||||||
|
.filter(item => item.deleted_at === null) // фильтруем по условию
|
||||||
|
.length > 0 ? (
|
||||||
|
filteredExercises
|
||||||
|
.filter(item => item.deleted_at === null)
|
||||||
|
|
||||||
|
*/}
|
||||||
|
|
||||||
<div className="exercise-list mb-20">
|
<div className="exercise-list mb-20">
|
||||||
{filteredExercises.length > 0 ? (
|
{filteredExercises.length > 0 ? (
|
||||||
filteredExercises.map((item) => (
|
filteredExercises.map((item, index) => (
|
||||||
<div
|
<div
|
||||||
key={item.id_exercise}
|
key={index}
|
||||||
onClick={() => history.push(getRouteExercise(item.id_course.toString(), item.id_exercise.toString()))}
|
onClick={() => 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>
|
||||||
<h3 className="text-sm">Упражнение {item.id_exercise}</h3>
|
<h3 className="text-sm">Упражнение {item.id_exercise}</h3>
|
||||||
<h3 className="text-xl font-semibold text-gray-600">{item.exercise.title}</h3>
|
<h3 className="text-xl font-semibold text-gray-600">{item.exercise.title}</h3>
|
||||||
</div>
|
</div>
|
||||||
{/* Иконка стрелки */}
|
{/* Иконка стрелки */}
|
||||||
<div className="text-[#2BACBE] transform transition-transform duration-300 hover:translate-x-1">
|
<div className="text-[#2BACBE] transform transition-transform duration-300 hover:translate-x-1">
|
||||||
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" />
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="h-0.5 w-full bg-gray-200 my-5"></div>
|
<div className="h-0.5 w-full bg-gray-200 my-5"></div>
|
||||||
|
@ -1,288 +0,0 @@
|
|||||||
"use client"
|
|
||||||
|
|
||||||
import { useState, useEffect } from "react"
|
|
||||||
import { useHistory, useParams } from "react-router-dom"
|
|
||||||
|
|
||||||
import HeaderNav from "../components/HeaderNav"
|
|
||||||
import BottomNavigation from "../components/BottomNavigation"
|
|
||||||
import video from "../assets/img/video.mov"
|
|
||||||
|
|
||||||
import { connect } from '../confconnect';
|
|
||||||
import { getRouteExercise } from "../shared/consts/router"
|
|
||||||
|
|
||||||
interface CourseExercise {
|
|
||||||
ID: number;
|
|
||||||
title: string;
|
|
||||||
desc: string;
|
|
||||||
url_file_img: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export const CourseExercises = () => {
|
|
||||||
const history = useHistory()
|
|
||||||
// const { id } = useParams<{ id: string }>();
|
|
||||||
const [currentSlide, setCurrentSlide] = useState(0);
|
|
||||||
const token = localStorage.getItem('authToken');
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
console.log(token)
|
|
||||||
if (!token) {
|
|
||||||
setError('Токен не найден');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
connect.get('/pacient/', {
|
|
||||||
headers: {
|
|
||||||
Authorization: `Bearer ${token}`,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.then(response => {
|
|
||||||
console.log('Response status:', response.status);
|
|
||||||
|
|
||||||
// Предполагаемая структура:
|
|
||||||
// response.data.courses — массив пользователей
|
|
||||||
const users = response.data.courses || [];
|
|
||||||
|
|
||||||
// Собираем все курсы из всех пользователей
|
|
||||||
const allCourses: Course[] = [];
|
|
||||||
|
|
||||||
users.forEach(user => {
|
|
||||||
if (user.Courses && Array.isArray(user.Courses)) {
|
|
||||||
user.Courses.forEach(course => {
|
|
||||||
// Можно добавить проверку или преобразование
|
|
||||||
allCourses.push({
|
|
||||||
ID: course.id, // или course.ID, зависит от структуры
|
|
||||||
title: course.title,
|
|
||||||
desc: course.desc,
|
|
||||||
url_file_img: course.url_file_img,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
setCourses(allCourses);
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
if (error.response) {
|
|
||||||
console.error('Ошибка ответа сервера:', error.response.status, error.response.data);
|
|
||||||
setError(`Ошибка сервера: ${error.response.status}`);
|
|
||||||
} else if (error.request) {
|
|
||||||
console.error('Нет ответа от сервера:', error.request);
|
|
||||||
setError('Нет ответа от сервера');
|
|
||||||
} else {
|
|
||||||
console.error('Ошибка при настройке запроса:', error.message);
|
|
||||||
setError(`Ошибка: ${error.message}`);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}, [token]);
|
|
||||||
|
|
||||||
|
|
||||||
const course = {
|
|
||||||
id: 2,
|
|
||||||
description: 'Очень сильно',
|
|
||||||
name: 'Качаем ноги',
|
|
||||||
exercises: [
|
|
||||||
{
|
|
||||||
id: 1,
|
|
||||||
name: "Подъемы ног лежа",
|
|
||||||
duration: "15 мин",
|
|
||||||
sets: 3,
|
|
||||||
reps: 12,
|
|
||||||
image: "/placeholder.svg?height=200&width=300",
|
|
||||||
difficulty: "Легкий",
|
|
||||||
description: "Укрепление мышц бедра и улучшение подвижности коленного сустава",
|
|
||||||
calories: 45,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 2,
|
|
||||||
name: "Приседания у стены",
|
|
||||||
duration: "10 мин",
|
|
||||||
sets: 2,
|
|
||||||
reps: 15,
|
|
||||||
image: "/placeholder.svg?height=200&width=300",
|
|
||||||
difficulty: "Средний",
|
|
||||||
description: "Безопасные приседания для восстановления силы ног",
|
|
||||||
calories: 60,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 3,
|
|
||||||
name: "Растяжка квадрицепса",
|
|
||||||
duration: "8 мин",
|
|
||||||
sets: 1,
|
|
||||||
reps: 30,
|
|
||||||
image: "/placeholder.svg?height=200&width=300",
|
|
||||||
difficulty: "Легкий",
|
|
||||||
description: "Улучшение гибкости и снятие напряжения",
|
|
||||||
calories: 25,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 4,
|
|
||||||
name: "Укрепление икр",
|
|
||||||
duration: "12 мин",
|
|
||||||
sets: 3,
|
|
||||||
reps: 20,
|
|
||||||
image: "/placeholder.svg?height=200&width=300",
|
|
||||||
difficulty: "Средний",
|
|
||||||
description: "Развитие силы и выносливости икроножных мышц",
|
|
||||||
calories: 40,
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
|
|
||||||
// Функции для переключения на следующее/предыдущее упражнение
|
|
||||||
const nextExercise = () => {
|
|
||||||
setCurrentSlide((prev) => (prev + 1) % course.exercises.length)
|
|
||||||
}
|
|
||||||
|
|
||||||
const prevExercise = () => {
|
|
||||||
setCurrentSlide((prev) => (prev - 1 + course.exercises.length) % course.exercises.length)
|
|
||||||
}
|
|
||||||
const currentExercise = course.exercises[currentSlide]
|
|
||||||
|
|
||||||
// const getDifficultyColor = (difficulty: string) => {
|
|
||||||
// switch (difficulty) {
|
|
||||||
// case "Легкий":
|
|
||||||
// return "bg-gradient-to-r from-emerald-400 to-green-500"
|
|
||||||
// case "Средний":
|
|
||||||
// return "bg-gradient-to-r from-amber-400 to-orange-500"
|
|
||||||
// case "Сложный":
|
|
||||||
// return "bg-gradient-to-r from-red-400 to-pink-500"
|
|
||||||
// default:
|
|
||||||
// return "bg-gradient-to-r from-gray-400 to-gray-500"
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
return (
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div className="bg-gray-50 w-full h-full overflow-auto">
|
|
||||||
<div className="my-36 min-h-screen max-w-4xl mx-auto">
|
|
||||||
<HeaderNav item={course.name} text={course.description} />
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{/* Exercise Slider */}
|
|
||||||
<div className="px-4 sm:px-6 mt-34 mb-10 ">
|
|
||||||
<div className="glass-morphism rounded-3xl border border-white/50 shadow-2xl overflow-hidden backdrop-blur-2xl relative">
|
|
||||||
{/* Exercise Image */}
|
|
||||||
<div className="relative">
|
|
||||||
<video
|
|
||||||
src={video}
|
|
||||||
className="w-full h-120 object-cover"
|
|
||||||
autoPlay
|
|
||||||
loop
|
|
||||||
muted
|
|
||||||
playsInline
|
|
||||||
/>
|
|
||||||
<div className="absolute inset-0 bg-gradient-to-t from-black/20 via-transparent to-black/10"></div>
|
|
||||||
|
|
||||||
{/* Difficulty Badge */}
|
|
||||||
<div className="absolute top-4 right-4">
|
|
||||||
{/* <div
|
|
||||||
className={`px-4 py-2 rounded-full text-xs font-bold text-white shadow-lg backdrop-blur-sm ${getDifficultyColor(currentExercise.difficulty)}`}
|
|
||||||
>
|
|
||||||
{currentExercise.difficulty}
|
|
||||||
</div> */}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Navigation arrows */}
|
|
||||||
|
|
||||||
<button
|
|
||||||
onClick={prevExercise}
|
|
||||||
className="z-50 absolute left-4 top-1/2 transform -translate-y-1/2 w-12 h-12 bg-black/20 backdrop-blur-xl rounded-full flex items-center justify-center text-white hover:bg-black/30 transition-all duration-300 shadow-lg border border-white/20"
|
|
||||||
>
|
|
||||||
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 19l-7-7 7-7" />
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
onClick={nextExercise}
|
|
||||||
className="z-50 absolute right-4 top-1/2 transform -translate-y-1/2 w-12 h-12 bg-black/20 backdrop-blur-xl rounded-full flex items-center justify-center text-white hover:bg-black/30 transition-all duration-300 shadow-lg border border-white/20"
|
|
||||||
>
|
|
||||||
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" />
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Exercise Info */}
|
|
||||||
<div className="p-6">
|
|
||||||
<h3 className="text-xl font-semibold text-gray-600 mb-3">{currentExercise.name}</h3>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<button
|
|
||||||
onClick={() => history.push(getRouteExercise(currentExercise.id))}
|
|
||||||
className="w-full bg-gradient-to-r bg-orange-400 text-white font-semibold py-4 px-6 rounded-2xl hover:shadow-2xl transition-all duration-300 transform hover:scale-105 shadow-lg backdrop-blur-sm"
|
|
||||||
>
|
|
||||||
Начать упражнение
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Slide indicators */}
|
|
||||||
<div className="flex justify-center mt-6 space-x-2">
|
|
||||||
{course.exercises.map((_, index) => (
|
|
||||||
<button
|
|
||||||
key={index}
|
|
||||||
onClick={() => setCurrentSlide(index)}
|
|
||||||
className={`h-4 rounded-full transition-all duration-300 ${index === currentSlide
|
|
||||||
? "bg-gradient-to-r from-[#2BACBE] to-cyan-600 w-8 shadow-lg"
|
|
||||||
: "bg-gray-300 w-4 hover:bg-gray-400"
|
|
||||||
}`}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{/* Exercise List */}
|
|
||||||
<div className="px-4 sm:px-6 mb-50">
|
|
||||||
<h2 className="text-xl sm:text-2xl font-semibold text-gray-800 mb-6">Все упражнения курса</h2>
|
|
||||||
<div className="space-y-4">
|
|
||||||
{course.exercises.map((exercise, index) => (
|
|
||||||
<div
|
|
||||||
key={exercise.id}
|
|
||||||
onClick={() => history.push(getRouteExercise(currentExercise.id))}
|
|
||||||
className={`glass-morphism rounded-2xl p-4 sm:p-6 border border-white/50 shadow-lg cursor-pointer transition-all duration-300 hover:shadow-2xl transform hover:scale-[1.02] backdrop-blur-xl ${index === currentSlide ? "ring-2 ring-[#2BACBE] bg-cyan-50/20" : ""
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
<div className="flex items-center space-x-4">
|
|
||||||
<div className="relative">
|
|
||||||
<div className="w-14 h-14 sm:w-16 sm:h-16 border-2 border-cyan-500 rounded-2xl flex items-center justify-center text-cyan-500 font-semibold text-lg sm:text-xl shadow-xl">
|
|
||||||
{index + 1}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<div className="flex-1 min-w-0">
|
|
||||||
<h3 className="font-semibold text-gray-800 text-lg sm:text-xl truncate">{exercise.name}</h3>
|
|
||||||
<p className="text-gray-600 text-sm mb-2 line-clamp-2">{exercise.description}</p>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<div className="text-[#2BACBE] transform transition-transform duration-300 hover:translate-x-1">
|
|
||||||
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" />
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<BottomNavigation />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -1,133 +0,0 @@
|
|||||||
"use client"
|
|
||||||
|
|
||||||
import { useState, useEffect } from 'react';
|
|
||||||
import BottomNavigation from "../components/BottomNavigation"
|
|
||||||
import { useHistory } from "react-router-dom"
|
|
||||||
import HeaderNav from "../components/HeaderNav"
|
|
||||||
import { getRouteCourseExercises } from "../shared/consts/router"
|
|
||||||
|
|
||||||
// Типизация Exercise
|
|
||||||
interface Exercise {
|
|
||||||
id: number;
|
|
||||||
title: string;
|
|
||||||
desc: string;
|
|
||||||
url_file_img: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const ProgressLine = () => {
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
className="h-full bg-gradient-to-r from-orange-300 to-orange-500 transition-all duration-500 absolute left-0 top-0 rounded-l-xl"
|
|
||||||
style={{ width: '85%' }}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const AnalitcsCards = () => {
|
|
||||||
return (
|
|
||||||
<div className="px-6 mb-8" >
|
|
||||||
<div className="text-center relative ">
|
|
||||||
<div className="w-full h-10 bg-gray-500 rounded-xl relative flex items-center justify-center mb-4 shadow-xl">
|
|
||||||
{/* Прогрессная часть */}
|
|
||||||
<ProgressLine />
|
|
||||||
|
|
||||||
{/* Текст поверх линии */}
|
|
||||||
<div className="absolute text-white font-semibold text-base">Вы прошли реабилитацию на {85}%</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export const Courses = () => {
|
|
||||||
const history = useHistory()
|
|
||||||
|
|
||||||
// Внутренний компонент для загрузки упражнений
|
|
||||||
const Exercises = () => {
|
|
||||||
const [exercises, setExercises] = useState<Exercise[]>([]);
|
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
fetch('/pacient/exercises')
|
|
||||||
.then(res => {
|
|
||||||
if (!res.ok) throw new Error('Ошибка при загрузке упражнений');
|
|
||||||
return res.json();
|
|
||||||
})
|
|
||||||
.then((data: Exercise[]) => {
|
|
||||||
console.log('test');
|
|
||||||
setExercises(data);
|
|
||||||
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
setError(err.message);
|
|
||||||
|
|
||||||
});
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
{/* Можно добавить заголовок или обертку */}
|
|
||||||
<ul>
|
|
||||||
{exercises.map(ex => (
|
|
||||||
<li key={ex.id} className="mb-4 p-4 bg-white/20 rounded-lg shadow-md hover:bg-white/30 transition">
|
|
||||||
<h3 className="font-semibold text-[#5F5C5C] mb-2">{ex.title}</h3>
|
|
||||||
<p className="text-sm text-[#5F5C5C]/70 mb-2">{ex.desc}</p>
|
|
||||||
{/* Вставка изображения */}
|
|
||||||
<img src={ex.url_file_img} alt={ex.title} className="w-full rounded" />
|
|
||||||
</li>
|
|
||||||
))}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="my-36 min-h-screen max-w-4xl mx-auto">
|
|
||||||
<HeaderNav item='Курсы' text='все назначенные' />
|
|
||||||
<AnalitcsCards />
|
|
||||||
|
|
||||||
{/* Внедрение компонента упражнений */}
|
|
||||||
<Exercises />
|
|
||||||
|
|
||||||
{/* Можно оставить закомментированный блок или убрать его */}
|
|
||||||
{/*
|
|
||||||
<div className="px-6 mb-8">
|
|
||||||
<div className="flex items-center justify-between mb-4">
|
|
||||||
<h2 className="text-xl font-black text-[#5F5C5C]">Мои курсы</h2>
|
|
||||||
<button onClick={() => history.push("/courses")} className="text-[#2BACBE] text-sm font-bold hover:underline">
|
|
||||||
Все курсы
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div className="space-y-4">
|
|
||||||
{courses.map((course) => (
|
|
||||||
<div
|
|
||||||
key={course.id}
|
|
||||||
onClick={() => history.push(getRouteCourseExercises(':id'))}
|
|
||||||
className="bg-white/30 backdrop-blur-2xl rounded-3xl p-6 border border-white/20 shadow-xl cursor-pointer hover:shadow-2xl transition-all duration-300 transform hover:scale-[1.02]"
|
|
||||||
>
|
|
||||||
<div className="flex items-center space-x-5">
|
|
||||||
|
|
||||||
<div className="flex-1">
|
|
||||||
<h3 className="font-semibold text-[#5F5C5C] text-lg mb-2">{course.name}</h3>
|
|
||||||
<div className="bg-white/50 rounded-full h-3 mb-2 overflow-hidden">
|
|
||||||
<div
|
|
||||||
className={`bg-gradient-to-r ${course.color} h-3 rounded-full transition-all duration-700 shadow-sm`}
|
|
||||||
style={{ width: `${course.progress}%` }}
|
|
||||||
></div>
|
|
||||||
</div>
|
|
||||||
<p className="text-sm text-[#5F5C5C]/70 font-semibold">{course.progress}% завершено</p>
|
|
||||||
</div>
|
|
||||||
<div className="text-[#2BACBE] transform transition-transform duration-300 hover:translate-x-1">
|
|
||||||
{/* Иконка стрелки */}
|
|
||||||
{/*<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"></path></svg>*/}
|
|
||||||
{/*</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div> */}
|
|
||||||
|
|
||||||
<BottomNavigation />
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
@ -1,9 +1,11 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import { useState, useEffect } from 'react';
|
import { useState, useEffect } from 'react';
|
||||||
import BottomNavigation from "../components/BottomNavigation"
|
import { useHistory } from "react-router-dom";
|
||||||
import { useHistory } from "react-router-dom"
|
|
||||||
import HeaderNav from "../components/HeaderNav"
|
import BottomNavigation from "../components/BottomNavigation";
|
||||||
|
|
||||||
|
import HeaderNav from "../components/HeaderNav";
|
||||||
import { connect } from '../confconnect';
|
import { connect } from '../confconnect';
|
||||||
import { getRouteCourseExercises } from '../shared/consts/router';
|
import { getRouteCourseExercises } from '../shared/consts/router';
|
||||||
|
|
||||||
@ -39,6 +41,10 @@ const AnalitcsCards = () => {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// НАЧАЛО КОМПОНЕНТА
|
||||||
|
|
||||||
export const Courses = () => {
|
export const Courses = () => {
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
|
|
||||||
@ -106,11 +112,7 @@ export const Courses = () => {
|
|||||||
|
|
||||||
// Цвета для прогресс-баров в оттенках cyan
|
// Цвета для прогресс-баров в оттенках cyan
|
||||||
const progressColors = [
|
const progressColors = [
|
||||||
"from-cyan-400 to-cyan-600",
|
"from-gray-400 to-cyan-800",
|
||||||
"from-cyan-500 to-cyan-700",
|
|
||||||
"from-teal-400 to-cyan-500",
|
|
||||||
"from-cyan-300 to-teal-600",
|
|
||||||
"from-sky-400 to-cyan-600"
|
|
||||||
];
|
];
|
||||||
|
|
||||||
//item.exercise.title
|
//item.exercise.title
|
||||||
|
@ -1,240 +0,0 @@
|
|||||||
"use client"
|
|
||||||
|
|
||||||
import { useState, useEffect } from "react"
|
|
||||||
import { useHistory } from "react-router-dom"
|
|
||||||
|
|
||||||
import BottomNavigation from "../components/BottomNavigation"
|
|
||||||
import { getRouteCourseComplete } from "../shared/consts/router"
|
|
||||||
import HeaderNav from "../components/HeaderNav"
|
|
||||||
import video from "../assets/img/video.mov"
|
|
||||||
|
|
||||||
|
|
||||||
export interface Exercise {
|
|
||||||
id: number;
|
|
||||||
title: string;
|
|
||||||
desc: string;
|
|
||||||
url_file: string;
|
|
||||||
url_file_img: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export const Exercise = () => {
|
|
||||||
const history = useHistory()
|
|
||||||
// const { id } = useParams<{ id: string }>()
|
|
||||||
const [isPlaying, setIsPlaying] = useState(false)
|
|
||||||
const [currentTime, setCurrentTime] = useState(0)
|
|
||||||
const [totalTime] = useState(900) // 15 minutes in seconds
|
|
||||||
const [currentSet, setCurrentSet] = useState(1)
|
|
||||||
const [totalSets] = useState(3)
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
let interval: NodeJS.Timeout | undefined
|
|
||||||
if (isPlaying) {
|
|
||||||
interval = setInterval(() => {
|
|
||||||
setCurrentTime((prev) => {
|
|
||||||
if (prev >= totalTime) {
|
|
||||||
setIsPlaying(false)
|
|
||||||
// Show completion animation
|
|
||||||
history.push(getRouteCourseComplete())
|
|
||||||
return totalTime
|
|
||||||
}
|
|
||||||
return prev + 1
|
|
||||||
})
|
|
||||||
}, 1000)
|
|
||||||
}
|
|
||||||
return () => {
|
|
||||||
if (interval) clearInterval(interval)
|
|
||||||
}
|
|
||||||
}, [isPlaying, totalTime, history])
|
|
||||||
|
|
||||||
const formatTime = (seconds: number) => {
|
|
||||||
const mins = Math.floor(seconds / 60)
|
|
||||||
const secs = seconds % 60
|
|
||||||
return `${mins.toString().padStart(2, "0")}:${secs.toString().padStart(2, "0")}`
|
|
||||||
}
|
|
||||||
|
|
||||||
const progress = (currentTime / totalTime) * 100
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const PlayIcon = () => (
|
|
||||||
<svg className="w-10 h-10 text-white" fill="currentColor" viewBox="0 0 24 24">
|
|
||||||
<path d="M8 5v14l11-7z" />
|
|
||||||
</svg>
|
|
||||||
)
|
|
||||||
|
|
||||||
const PauseIcon = () => (
|
|
||||||
<svg className="w-10 h-10 text-white" fill="currentColor" viewBox="0 0 24 24">
|
|
||||||
<path d="M6 19h4V5H6v14zm8-14v14h4V5h-4z" />
|
|
||||||
</svg>
|
|
||||||
)
|
|
||||||
|
|
||||||
const RefreshIcon = () => (
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="#6F6F6F" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"> <path d="M2.5 2v6h6M21.5 22v-6h-6" /><path d="M22 11.5A10 10 0 0 0 3.2 7.2M2 12.5a10 10 0 0 0 18.8 4.2" /></svg>
|
|
||||||
)
|
|
||||||
|
|
||||||
const CheckIcon = () => (
|
|
||||||
<svg className="w-6 h-6 text-cyan-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={3} d="M5 13l4 4L19 7" />
|
|
||||||
</svg>
|
|
||||||
)
|
|
||||||
|
|
||||||
const exerciseSteps = [
|
|
||||||
{
|
|
||||||
title: "Исходное положение",
|
|
||||||
description:
|
|
||||||
"Лягте на спирку на коврик. Руки вдоль тела, ладони прижаты к полу. Ноги выпрямлены, носки направлены вверх. Поясница плотно прижата к полу.",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Задание",
|
|
||||||
description:
|
|
||||||
"Медленно поднимите прямые ноги до угла 90 градусов. Задержитесь на 2 секунды, затем медленно опустите ноги, не касаясь пола. Повторите движение плавно и контролируемо.",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Подходы",
|
|
||||||
description: "Выполните 3 подхода по 12 повторений с отдыхом 60 секунд между подходами.",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Перерыв",
|
|
||||||
description: "Отдыхайте 60 секунд между подходами. Дышите спокойно и расслабьте мышцы.",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Динамика и статика",
|
|
||||||
description:
|
|
||||||
"Динамическая фаза: подъем и опускание ног выполняется плавно, 2 секунды вверх, 2 секунды вниз. Статическая фаза: удержание ног в верхней точке на 2 секунды.",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
|
||||||
|
|
||||||
<div className="bg-gray-50 w-full h-full overflow-auto">
|
|
||||||
<div className="mt-36 mb-90 min-h-screen max-w-4xl mx-auto">
|
|
||||||
|
|
||||||
<HeaderNav item='Надо разобраться?' text='упражнение' />
|
|
||||||
|
|
||||||
|
|
||||||
<div className="px-4 sm:px-6 mt-10 mb-6">
|
|
||||||
<div className="glass-morphism rounded-3xl overflow-hidden shadow-2xl border border-white/20 backdrop-blur-2xl">
|
|
||||||
<div className="relative">
|
|
||||||
<video
|
|
||||||
src={video}
|
|
||||||
className="w-full h-120 object-cover"
|
|
||||||
autoPlay
|
|
||||||
loop
|
|
||||||
muted
|
|
||||||
playsInline
|
|
||||||
/>
|
|
||||||
<div className="absolute inset-0 bg-gradient-to-t from-black/30 via-transparent to-black/10"></div>
|
|
||||||
<div className="absolute inset-0 flex items-center justify-center">
|
|
||||||
<button
|
|
||||||
onClick={() => setIsPlaying(!isPlaying)}
|
|
||||||
className={
|
|
||||||
isPlaying
|
|
||||||
? "w-20 h-20 bg-transparent backdrop-blur-xl rounded-full flex items-center justify-center shadow-2xl transition-all duration-300 transform hover:scale-110 border border-cyan-50 opacity-40"
|
|
||||||
: "rounded-full w-20 h-20 flex items-center justify-center backdrop-blur-xl opacity-70"
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{isPlaying ? <PauseIcon /> : <PlayIcon />}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{isPlaying && (
|
|
||||||
<div className="absolute top-4 left-4 flex items-center space-x-2">
|
|
||||||
<div className="w-3 h-3 bg-orange-500 rounded-full animate-pulse "></div>
|
|
||||||
<span className="text-white text-sm font-bold bg-black/30 px-3 py-1 rounded-full backdrop-blur-sm">
|
|
||||||
Выполнение
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<div className="absolute top-4 right-4 bg-cyan-50 backdrop-blur-sm px-3 py-1 rounded-xl">
|
|
||||||
<span className="text-gray-800 text-sm font-bold">{formatTime(currentTime)}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="px-4 sm:px-6 space-y-4 mb-6">
|
|
||||||
{exerciseSteps.map((step, index) => (
|
|
||||||
<div
|
|
||||||
key={index}
|
|
||||||
className="bg-white rounded-2xl p-5 border border-gray-200 shadow-lg hover:shadow-xl transition-all duration-300"
|
|
||||||
>
|
|
||||||
<div className="flex items-start space-x-4">
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<h3 className="text-lg font-black text-gray-800 mb-2">{step.title}</h3>
|
|
||||||
<p className="text-gray-600 leading-relaxed text-sm sm:text-base">{step.description}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Fixed Timer at Bottom */}
|
|
||||||
<div className="fixed bottom-36 left-0 right-0 bg-white opacity-95 border-t border-white/20 px-4 sm:px-6 py-4 shadow-2xl z-30">
|
|
||||||
<div className="max-w-md mx-auto">
|
|
||||||
<div className="flex items-center justify-between mb-3">
|
|
||||||
<div className="flex items-center space-x-2">
|
|
||||||
<div className="w-2 h-2 bg-green-400 rounded-full animate-pulse"></div>
|
|
||||||
<span className="text-sm font-bold text-gray-700">
|
|
||||||
Подход {currentSet} из {totalSets}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<span className="text-sm font-black text-gray-700">
|
|
||||||
{formatTime(currentTime)} / {formatTime(totalTime)}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div className="bg-white/30 rounded-full h-3 mb-4 overflow-hidden">
|
|
||||||
<div
|
|
||||||
className="bg-gradient-to-r from-[#2BACBE] via-cyan-500 to-cyan-700 h-3 rounded-full transition-all duration-1000 shadow-sm"
|
|
||||||
style={{ width: `${progress}%` }}
|
|
||||||
></div>
|
|
||||||
</div>
|
|
||||||
<div className="flex space-x-3">
|
|
||||||
<button
|
|
||||||
onClick={() => setIsPlaying(!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 ${isPlaying
|
|
||||||
? "bg-gray-400 text-white shadow-lg"
|
|
||||||
: "bg-[#2BACBE] hover:bg-[#2099A8] text-white shadow-lg"
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
{isPlaying ? (
|
|
||||||
<>
|
|
||||||
<PauseIcon /> <span>Пауза</span>
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
<PlayIcon /> <span className="backdrop-blur-xl">Начать</span>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
onClick={() => {
|
|
||||||
setCurrentTime(0)
|
|
||||||
setIsPlaying(false)
|
|
||||||
}}
|
|
||||||
className="px-6 py-3 bg-white text-gray-800 font-bold rounded-xl transition-all duration-300 hover:shadow-lg border border-gray-200 flex items-center justify-center"
|
|
||||||
>
|
|
||||||
<RefreshIcon />
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
onClick={() => setCurrentSet((prev) => Math.min(prev + 1, totalSets))}
|
|
||||||
className="px-6 py-3 bg-white text-gray-800 font-bold rounded-xl transition-all duration-300 hover:shadow-lg border border-gray-200 flex items-center justify-center"
|
|
||||||
>
|
|
||||||
<CheckIcon />
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<BottomNavigation />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -1,15 +1,21 @@
|
|||||||
"use client"
|
"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 HeaderNav from "../components/HeaderNav";
|
||||||
|
import BottomNavigation from "../components/BottomNavigation";
|
||||||
|
|
||||||
|
// import { getRouteCourseComplete } from "../shared/consts/router";
|
||||||
|
import { connect } from "../confconnect";
|
||||||
|
|
||||||
|
|
||||||
|
//В TypeScript ключевое слово interface используется для определения интерфейсов — это способ описать структуру объектов, то есть какие свойства и методы у них есть, и какие типы данных они содержат
|
||||||
|
|
||||||
import { CheckIcon } from "../components/icons/CheckIcon"
|
|
||||||
import { RefreshIcon } from "../components/icons/RefreshIcon"
|
|
||||||
import BottomNavigation from "../components/BottomNavigation"
|
|
||||||
import { getRouteCourseComplete } from "../shared/consts/router"
|
|
||||||
import HeaderNav from "../components/HeaderNav"
|
|
||||||
import video from "../assets/img/video.mov"
|
|
||||||
import { connect } from "../confconnect"
|
|
||||||
|
|
||||||
export interface Exercise {
|
export interface Exercise {
|
||||||
id: number
|
id: number
|
||||||
@ -115,7 +121,7 @@ export const Exercise = () => {
|
|||||||
// Отправляем результат на сервер при завершении
|
// Отправляем результат на сервер при завершении
|
||||||
submitProgress()
|
submitProgress()
|
||||||
// Show completion animation
|
// Show completion animation
|
||||||
history.push(getRouteCourseComplete())
|
// history.push(getRouteCourseComplete())
|
||||||
return totalTime
|
return totalTime
|
||||||
}
|
}
|
||||||
return prev + 1
|
return prev + 1
|
||||||
@ -257,7 +263,7 @@ export const Exercise = () => {
|
|||||||
return (
|
return (
|
||||||
<div className="bg-gray-50 w-full h-full overflow-auto">
|
<div className="bg-gray-50 w-full h-full overflow-auto">
|
||||||
<div className="mt-36 mb-90 min-h-screen max-w-4xl mx-auto">
|
<div className="mt-36 mb-90 min-h-screen max-w-4xl mx-auto">
|
||||||
<HeaderNav item={exercise.title} text={`День ${exercise.day}, позиция ${exercise.position}`} />
|
<HeaderNav item={exercise.title} text={`День ${exercise.day}, упражнение ${exercise.position}`} />
|
||||||
|
|
||||||
<div className="px-4 sm:px-6 mt-10 mb-6">
|
<div className="px-4 sm:px-6 mt-10 mb-6">
|
||||||
<div className="glass-morphism rounded-3xl overflow-hidden shadow-2xl border border-white/20 backdrop-blur-2xl">
|
<div className="glass-morphism rounded-3xl overflow-hidden shadow-2xl border border-white/20 backdrop-blur-2xl">
|
||||||
@ -384,9 +390,6 @@ export const Exercise = () => {
|
|||||||
|
|
||||||
className={`cursor-pointer px-6 py-3 font-bold rounded-xl transition-all duration-500 hover:scale-105 hover:shadow-lg border border-gray-200 flex items-center justify-center ${!isActive ? "bg-white text-cyan-500" : "bg-orange-400 text-white"}`}
|
className={`cursor-pointer px-6 py-3 font-bold rounded-xl transition-all duration-500 hover:scale-105 hover:shadow-lg border border-gray-200 flex items-center justify-center ${!isActive ? "bg-white text-cyan-500" : "bg-orange-400 text-white"}`}
|
||||||
>
|
>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<CheckIcon
|
<CheckIcon
|
||||||
className="w-6 h-6"
|
className="w-6 h-6"
|
||||||
|
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import type React from "react"
|
import type React from "react";
|
||||||
import { useState } from "react"
|
import { useState } from "react";
|
||||||
import { useHistory } from "react-router-dom"
|
import { useHistory } from "react-router-dom";
|
||||||
|
|
||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
import { connect } from '../confconnect';
|
import { connect } from '../confconnect';
|
||||||
|
@ -1,24 +1,23 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import { useEffect, useState } from "react"
|
import { useEffect, useState } from "react";
|
||||||
import { useHistory } from "react-router-dom"
|
import { useHistory } from "react-router-dom";
|
||||||
|
|
||||||
|
import { CalendarIcon } from "../components/icons/CalendarIcon";
|
||||||
|
import { DumbbellIcon } from "../components/icons/DumbbellIcon";
|
||||||
|
|
||||||
|
|
||||||
import { CalendarIcon } from "../components/icons/CalendarIcon"
|
import HeaderNav from "../components/HeaderNav";
|
||||||
import { DumbbellIcon } from "../components/icons/DumbbellIcon"
|
import BottomNavigation from "../components/BottomNavigation";
|
||||||
|
import CircularProgressDisplay from "../components/CircularProgressDisplay";
|
||||||
|
|
||||||
|
|
||||||
import HeaderNav from "../components/HeaderNav"
|
import { StatCardHome } from "../components/cards/StatCardHome";
|
||||||
import BottomNavigation from "../components/BottomNavigation"
|
import { WorkoutCardHome } from "../components/cards/WorkoutCardHome";
|
||||||
import CircularProgressDisplay from "../components/CircularProgressDisplay"
|
|
||||||
|
|
||||||
|
import { getRouteExercise } from "../shared/consts/router";
|
||||||
import { StatCardHome } from "../components/cards/StatCardHome"
|
import { getRouteCourses } from "../shared/consts/router";
|
||||||
import { WorkoutCardHome } from "../components/cards/WorkoutCardHome"
|
import { getRouteCourseExercises } from "../shared/consts/router";
|
||||||
|
|
||||||
import { getRouteExercise } from "../shared/consts/router"
|
|
||||||
import { getRouteCourses } from "../shared/consts/router"
|
|
||||||
import { getRouteCourseExercises } from "../shared/consts/router"
|
|
||||||
|
|
||||||
export default function Home() {
|
export default function Home() {
|
||||||
const history = useHistory()
|
const history = useHistory()
|
||||||
@ -67,7 +66,7 @@ export default function Home() {
|
|||||||
const overallProgress = Math.round(courses.reduce((sum, course) => sum + course.progress, 0) / totalCourses)
|
const overallProgress = Math.round(courses.reduce((sum, course) => sum + course.progress, 0) / totalCourses)
|
||||||
|
|
||||||
const handleWorkoutClick = () => {
|
const handleWorkoutClick = () => {
|
||||||
history.push(getRouteExercise(":id"))
|
history.push(getRouteExercise())
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleBackClick = () => {
|
const handleBackClick = () => {
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import React, { useState } from "react"
|
import React, { useState } from "react";
|
||||||
import { useHistory } from "react-router-dom"
|
import { useHistory } from "react-router-dom";
|
||||||
import manImage from "../assets/img/man.svg"
|
import manImage from "../assets/img/man.svg";
|
||||||
|
|
||||||
import { connect } from '../confconnect';
|
import { connect } from '../confconnect';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
import { getRouteHome } from '../shared/consts/router'
|
import { getRouteHome } from '../shared/consts/router';
|
||||||
import { getRouteForgotPassword } from '../shared/consts/router'
|
import { getRouteForgotPassword } from '../shared/consts/router';
|
||||||
|
|
||||||
|
|
||||||
export default function LoginPage() {
|
export default function LoginPage() {
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import type React from "react"
|
import type React from "react";
|
||||||
import { useHistory } from "react-router-dom"
|
import { useHistory } from "react-router-dom";
|
||||||
import BottomNavigation from "../components/BottomNavigation"
|
import BottomNavigation from "../components/BottomNavigation";
|
||||||
|
|
||||||
import { getRouteLogin } from "../shared/consts/router"
|
import { getRouteLogin } from "../shared/consts/router";
|
||||||
import manImage from "../assets/img/man.svg"
|
import manImage from "../assets/img/man.svg";
|
||||||
|
|
||||||
const Settings: React.FC = () => {
|
const Settings: React.FC = () => {
|
||||||
const history = useHistory()
|
const history = useHistory()
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import type React from "react"
|
import type React from "react";
|
||||||
import { useEffect, useState } from "react"
|
import { useEffect, useState } from "react";
|
||||||
import { useIonRouter } from '@ionic/react';
|
import { useIonRouter } from '@ionic/react';
|
||||||
|
|
||||||
const Welcome: React.FC = () => {
|
const Welcome: React.FC = () => {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user