реализована логика и отображение прогресса подходов и отдыха
This commit is contained in:
parent
3d5cd3a4ac
commit
359fbb1731
@ -1,22 +1,14 @@
|
||||
"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 { 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";
|
||||
import axios from "axios"
|
||||
|
||||
|
||||
//В TypeScript ключевое слово interface используется для определения интерфейсов — это способ описать структуру объектов, то есть какие свойства и методы у них есть, и какие типы данных они содержат
|
||||
import HeaderNav from "../components/HeaderNav"
|
||||
import BottomNavigation from "../components/BottomNavigation"
|
||||
|
||||
import { connect } from "../confconnect"
|
||||
|
||||
export interface Exercise {
|
||||
id: number
|
||||
@ -45,30 +37,43 @@ export const Exercise = () => {
|
||||
const [error, setError] = useState<string>("")
|
||||
const [isPlaying, setIsPlaying] = useState(false)
|
||||
const [currentTime, setCurrentTime] = useState(0)
|
||||
const [totalTime, setTotalTime] = useState(900) // Default 15 minutes
|
||||
const [totalTime, setTotalTime] = useState(900)
|
||||
const [currentSet, setCurrentSet] = useState(1)
|
||||
const [isActive, setIsActive] = useState(false)
|
||||
const [totalSets, setTotalSets] = useState(3)
|
||||
const [isRotating, setIsRotating] = useState(false)
|
||||
const [hasSavedProgress, setHasSavedProgress] = useState(false)
|
||||
const [isCompleted, setIsCompleted] = useState(false)
|
||||
const [completedSets, setCompletedSets] = useState<number[]>([])
|
||||
|
||||
|
||||
|
||||
//Можно реализовать полностью на клиенте.
|
||||
// Ограничение — прогресс сохраняется только на устройстве пользователя.
|
||||
// Если нужно синхронизировать между устройствами или сохранять историю, потребуется бэкенд.
|
||||
//сохраняем прогресс
|
||||
// Новые состояния для отдыха
|
||||
const [isResting, setIsResting] = useState(false)
|
||||
const [restTime, setRestTime] = useState(0)
|
||||
const [totalRestTime] = useState(60) // 60 секунд отдыха по умолчанию
|
||||
|
||||
// Восстановление прогресса при загрузке
|
||||
useEffect(() => {
|
||||
const savedProgress = localStorage.getItem('exerciseProgress');
|
||||
const savedProgress = localStorage.getItem("exerciseProgress")
|
||||
if (savedProgress) {
|
||||
const progress = JSON.parse(savedProgress);
|
||||
const progress = JSON.parse(savedProgress)
|
||||
if (progress.exerciseId === exercise?.id) {
|
||||
setCurrentTime(progress.position);
|
||||
setCurrentSet(progress.set);
|
||||
if (progress.status === 1) {
|
||||
setIsCompleted(true)
|
||||
setCurrentTime(progress.totalTime || totalTime)
|
||||
setCompletedSets(progress.completedSets || [])
|
||||
setCurrentSet(progress.set || totalSets)
|
||||
setHasSavedProgress(false)
|
||||
} else {
|
||||
setCurrentTime(progress.position)
|
||||
setCurrentSet(progress.set)
|
||||
setCompletedSets(progress.completedSets || [])
|
||||
setIsResting(progress.isResting || false)
|
||||
setRestTime(progress.restTime || 0)
|
||||
setHasSavedProgress(true)
|
||||
setIsCompleted(false)
|
||||
}
|
||||
}
|
||||
}, [exercise]);
|
||||
}
|
||||
}, [exercise, totalTime, totalSets])
|
||||
|
||||
useEffect(() => {
|
||||
console.log("Course ID:", courseId)
|
||||
@ -80,13 +85,9 @@ export const Exercise = () => {
|
||||
return
|
||||
}
|
||||
|
||||
// Получаем данные упражнения через API: GET /pacient/:course_id/:exercise_id
|
||||
connect
|
||||
.get(`pacient/${courseId}/${exerciseId}`)
|
||||
.then((response) => {
|
||||
// console.log("Response status:", response.status)
|
||||
// console.log("Response data:", response.data)
|
||||
|
||||
const exerciseData = response.data
|
||||
|
||||
setExercise({
|
||||
@ -103,9 +104,10 @@ export const Exercise = () => {
|
||||
sessionname: exerciseData.sessionname,
|
||||
})
|
||||
|
||||
// Устанавливаем время и подходы из данных упражнения
|
||||
if (exerciseData.time) {
|
||||
setTotalTime(Number.parseInt(exerciseData.time) * 60) // Конвертируем минуты в секунды
|
||||
const timeInSeconds = Number.parseInt(exerciseData.time) * 60
|
||||
setTotalTime(timeInSeconds)
|
||||
console.log("Установлено время упражнения:", timeInSeconds, "секунд")
|
||||
}
|
||||
if (exerciseData.count) {
|
||||
setTotalSets(exerciseData.count)
|
||||
@ -131,30 +133,104 @@ export const Exercise = () => {
|
||||
})
|
||||
}, [courseId, exerciseId])
|
||||
|
||||
// Функция для завершения текущего подхода
|
||||
const handleCompleteSet = async () => {
|
||||
console.log("Завершение подхода", currentSet, "из", totalSets)
|
||||
|
||||
const newCompletedSets = [...completedSets, currentSet]
|
||||
setCompletedSets(newCompletedSets)
|
||||
setIsPlaying(false)
|
||||
|
||||
// Проверяем, завершены ли все подходы
|
||||
if (newCompletedSets.length >= totalSets) {
|
||||
console.log("Все подходы завершены, завершаем упражнение")
|
||||
// Все подходы завершены - завершаем упражнение
|
||||
await handleComplete(newCompletedSets)
|
||||
} else {
|
||||
console.log("Начинаем отдых перед подходом", currentSet + 1)
|
||||
// Начинаем отдых перед следующим подходом
|
||||
setIsResting(true)
|
||||
setRestTime(totalRestTime)
|
||||
setCurrentTime(0)
|
||||
|
||||
const handlePause = async () => {
|
||||
// Формируем данные в том формате, который ожидает сервер
|
||||
const progressData = {
|
||||
time_users: formatTime(currentTime), // Отправляем как строку в формате MM:SS
|
||||
status: currentSet, // Отправляем текущий подход как статус
|
||||
// Сохраняем прогресс с отдыхом
|
||||
await handlePause(newCompletedSets, currentSet, true, totalRestTime)
|
||||
}
|
||||
}
|
||||
|
||||
// Функция для завершения упражнения
|
||||
const handleComplete = async (completedSetsArray = completedSets) => {
|
||||
console.log("Завершение упражнения")
|
||||
|
||||
const completionData = {
|
||||
time_users: formatTime(totalTime),
|
||||
status: 1,
|
||||
}
|
||||
|
||||
localStorage.setItem(
|
||||
"exerciseProgress",
|
||||
JSON.stringify({
|
||||
exerciseId: exercise?.id,
|
||||
position: totalTime,
|
||||
set: totalSets,
|
||||
status: 1,
|
||||
totalTime: totalTime,
|
||||
completedSets: completedSetsArray,
|
||||
completedAt: new Date().toISOString(),
|
||||
}),
|
||||
)
|
||||
|
||||
setIsCompleted(true)
|
||||
setCurrentTime(totalTime)
|
||||
setIsPlaying(false)
|
||||
setIsResting(false)
|
||||
setHasSavedProgress(false)
|
||||
setCompletedSets(completedSetsArray)
|
||||
|
||||
try {
|
||||
console.log("Отправляем данные о завершении:", completionData)
|
||||
|
||||
const response = await connect.post(`pacient/${courseId}/${exerciseId}`, completionData)
|
||||
|
||||
console.log("Ответ сервера при завершении:", response.status)
|
||||
console.log("Упражнение успешно завершено")
|
||||
} catch (error) {
|
||||
console.error("Ошибка при отправке завершения:", error)
|
||||
if (error.response) {
|
||||
console.error("Ответ сервера:", error.response.status, error.response.data)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const handlePause = async (
|
||||
completedSetsArray = completedSets,
|
||||
setNumber = currentSet,
|
||||
resting = isResting,
|
||||
currentRestTime = restTime,
|
||||
) => {
|
||||
const progressData = {
|
||||
time_users: formatTime(currentTime),
|
||||
status: setNumber,
|
||||
}
|
||||
|
||||
// Сохраняем в localStorage
|
||||
localStorage.setItem(
|
||||
"exerciseProgress",
|
||||
JSON.stringify({
|
||||
exerciseId: exercise?.id,
|
||||
position: currentTime,
|
||||
set: currentSet,
|
||||
set: setNumber,
|
||||
status: 0,
|
||||
completedSets: completedSetsArray,
|
||||
isResting: resting,
|
||||
restTime: currentRestTime,
|
||||
}),
|
||||
)
|
||||
|
||||
setHasSavedProgress(true)
|
||||
|
||||
try {
|
||||
console.log("Отправляем данные:", progressData)
|
||||
|
||||
// Используем connect (axios) вместо прямого axios
|
||||
const response = await connect.post(`pacient/${courseId}/${exerciseId}`, progressData)
|
||||
|
||||
console.log("Ответ сервера при отправке прогресса:", response.status)
|
||||
@ -167,38 +243,67 @@ const handlePause = async () => {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Основной таймер для упражнения
|
||||
useEffect(() => {
|
||||
let interval: NodeJS.Timeout | undefined
|
||||
if (isPlaying) {
|
||||
if (isPlaying && !isCompleted && !isResting) {
|
||||
interval = setInterval(() => {
|
||||
setCurrentTime((prev) => {
|
||||
if (prev >= totalTime) {
|
||||
const newTime = prev + 1
|
||||
console.log(`Таймер: ${newTime}/${totalTime} секунд`)
|
||||
|
||||
// Проверяем, достигли ли мы конца времени для текущего подхода
|
||||
if (newTime >= totalTime) {
|
||||
console.log("Время подхода истекло, завершаем подход")
|
||||
setIsPlaying(false)
|
||||
// Отправляем результат на сервер при завершении
|
||||
// submitProgress()
|
||||
// Show completion animation
|
||||
// history.push(getRouteCourseComplete())
|
||||
// Используем setTimeout чтобы избежать проблем с состоянием в useEffect
|
||||
setTimeout(() => {
|
||||
handleCompleteSet()
|
||||
}, 100)
|
||||
return totalTime
|
||||
}
|
||||
return prev + 1
|
||||
return newTime
|
||||
})
|
||||
}, 1000)
|
||||
}
|
||||
return () => {
|
||||
if (interval) clearInterval(interval)
|
||||
}
|
||||
}, [isPlaying, totalTime, history])
|
||||
}, [isPlaying, totalTime, isCompleted, isResting, currentSet, completedSets])
|
||||
|
||||
// Таймер для отдыха
|
||||
useEffect(() => {
|
||||
let restInterval: NodeJS.Timeout | undefined
|
||||
if (isResting && restTime > 0) {
|
||||
restInterval = setInterval(() => {
|
||||
setRestTime((prev) => {
|
||||
const newRestTime = prev - 1
|
||||
console.log(`Отдых: ${newRestTime} секунд осталось`)
|
||||
|
||||
if (newRestTime <= 0) {
|
||||
console.log("Отдых закончен, переходим к следующему подходу")
|
||||
// Отдых закончен, переходим к следующему подходу
|
||||
setIsResting(false)
|
||||
setCurrentSet(currentSet + 1)
|
||||
setCurrentTime(0)
|
||||
return 0
|
||||
}
|
||||
return newRestTime
|
||||
})
|
||||
}, 1000)
|
||||
}
|
||||
return () => {
|
||||
if (restInterval) clearInterval(restInterval)
|
||||
}
|
||||
}, [isResting, restTime, currentSet])
|
||||
|
||||
const handleClick = () => {
|
||||
setIsRotating(true);
|
||||
setIsRotating(true)
|
||||
}
|
||||
|
||||
const handleTransitionEnd = () => {
|
||||
setIsRotating(false);
|
||||
};
|
||||
|
||||
|
||||
setIsRotating(false)
|
||||
}
|
||||
|
||||
const formatTime = (seconds: number) => {
|
||||
const mins = Math.floor(seconds / 60)
|
||||
@ -206,7 +311,7 @@ const handlePause = async () => {
|
||||
return `${mins.toString().padStart(2, "0")}:${secs.toString().padStart(2, "0")}`
|
||||
}
|
||||
|
||||
const progress = (currentTime / totalTime) * 100
|
||||
const progress = isResting ? ((totalRestTime - restTime) / totalRestTime) * 100 : (currentTime / totalTime) * 100
|
||||
|
||||
const PlayIcon = () => (
|
||||
<svg className="w-10 h-10 text-white" fill="currentColor" viewBox="0 0 24 24">
|
||||
@ -220,9 +325,18 @@ const handlePause = async () => {
|
||||
</svg>
|
||||
)
|
||||
|
||||
const CheckIcon = () => (
|
||||
<svg className="w-10 h-10 text-white" fill="currentColor" viewBox="0 0 24 24">
|
||||
<path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z" />
|
||||
</svg>
|
||||
)
|
||||
|
||||
const RestIcon = () => (
|
||||
<svg className="w-10 h-10 text-white" fill="currentColor" viewBox="0 0 24 24">
|
||||
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z" />
|
||||
</svg>
|
||||
)
|
||||
|
||||
// Генерируем инструкции на основе реальных данных упражнения
|
||||
const exerciseSteps = [
|
||||
{
|
||||
title: "Описание упражнения",
|
||||
@ -304,28 +418,72 @@ const handlePause = async () => {
|
||||
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={exercise.title} text={`упражнение ${exercise.position}`}
|
||||
/>
|
||||
<HeaderNav item={exercise.title} text={`упражнение ${exercise.position}`} />
|
||||
|
||||
|
||||
<p className="bg-red-400 font-bold">Если упражнение меньше минуты, то скидывает сразу на отдых</p>
|
||||
<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">
|
||||
<img src={exercise.url_file_img || video} alt={exercise.title} className="w-full h-120 object-cover" />
|
||||
<img
|
||||
src={exercise.url_file_img || "/placeholder.svg?height=300&width=400&text=Упражнение"}
|
||||
alt={exercise.title}
|
||||
className="w-full h-64 object-cover"
|
||||
onError={(e) => {
|
||||
const target = e.target as HTMLImageElement
|
||||
target.src = "/placeholder.svg?height=300&width=400&text=Упражнение"
|
||||
}}
|
||||
/>
|
||||
<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">
|
||||
{isCompleted ? (
|
||||
<div className="w-20 h-20 bg-green-500 rounded-full flex items-center justify-center shadow-2xl">
|
||||
<CheckIcon />
|
||||
</div>
|
||||
) : isResting ? (
|
||||
<div className="w-20 h-20 bg-cyan-500 opacity-80 rounded-full flex items-center justify-center shadow-2xl">
|
||||
|
||||
<svg width="40" height="40" viewBox="0 0 24 24" fill="ffffff80" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M12 2C17.523 2 22 6.477 22 12C22 17.523 17.523 22 12 22C6.477 22 2 17.523 2 12C2 6.477 6.477 2 12 2ZM12 6C11.7348 6 11.4804 6.10536 11.2929 6.29289C11.1054 6.48043 11 6.73478 11 7V12C11.0001 12.2652 11.1055 12.5195 11.293 12.707L14.293 15.707C14.4816 15.8892 14.7342 15.99 14.9964 15.9877C15.2586 15.9854 15.5094 15.8802 15.6948 15.6948C15.8802 15.5094 15.9854 15.2586 15.9877 14.9964C15.99 14.7342 15.8892 14.4816 15.707 14.293L13 11.586V7C13 6.73478 12.8946 6.48043 12.7071 6.29289C12.5196 6.10536 12.2652 6 12 6Z" fill="#ffffff80"/>
|
||||
</svg>
|
||||
|
||||
</div>
|
||||
) : (
|
||||
<button
|
||||
onClick={() => setIsPlaying(!isPlaying)}
|
||||
className={
|
||||
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
|
||||
? "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"
|
||||
}
|
||||
? "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" : ""}`}
|
||||
>
|
||||
{isPlaying ? <PauseIcon /> : <PlayIcon />}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{isPlaying && (
|
||||
{/* Статус выполнения */}
|
||||
{isCompleted && (
|
||||
<div className="absolute top-4 left-4 flex items-center space-x-2">
|
||||
<div className="w-3 h-3 bg-green-500 rounded-full"></div>
|
||||
<span className="text-white text-sm font-bold bg-green-600/80 px-3 py-1 rounded-full backdrop-blur-sm">
|
||||
Выполнено
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{isResting && (
|
||||
<div className="absolute top-4 left-4 flex items-center space-x-2">
|
||||
<div className="w-3 h-3 bg-cyan-500 rounded-full animate-pulse"></div>
|
||||
<span className="text-white text-sm font-bold bg-cyan-600/80 px-3 py-1 rounded-full backdrop-blur-sm">
|
||||
Отдых
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{isPlaying && !isCompleted && !isResting && (
|
||||
<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">
|
||||
@ -335,12 +493,43 @@ const handlePause = async () => {
|
||||
)}
|
||||
|
||||
<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>
|
||||
<span className="text-gray-800 text-sm font-bold">
|
||||
{isResting ? formatTime(restTime) : formatTime(currentTime)}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Индикатор завершенных подходов */}
|
||||
<div className="px-4 sm:px-6 mb-6">
|
||||
<div className="bg-white rounded-2xl p-4 border border-gray-200 shadow-lg">
|
||||
<h3 className="text-lg font-bold text-gray-800 mb-3">Прогресс подходов</h3>
|
||||
<div className="flex space-x-2">
|
||||
{Array.from({ length: totalSets }, (_, index) => {
|
||||
const setNumber = index + 1
|
||||
const isSetCompleted = completedSets.includes(setNumber)
|
||||
const isCurrent = setNumber === currentSet && !isSetCompleted
|
||||
|
||||
return (
|
||||
<div key={setNumber} className="flex-1 text-center">
|
||||
<div
|
||||
className={`h-3 rounded-full transition-all duration-300 ${
|
||||
isSetCompleted ? "bg-green-500" : isCurrent ? "bg-cyan-500" : "bg-gray-200"
|
||||
}`}
|
||||
/>
|
||||
<div className="text-xs font-bold mt-1 text-gray-600">{setNumber}</div>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
<div className="flex justify-between text-xs text-gray-600 mt-2">
|
||||
<span>Завершено: {completedSets.length}</span>
|
||||
<span>Всего: {totalSets}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="px-4 sm:px-6 space-y-4 mb-6">
|
||||
{exerciseSteps.map((step, index) => (
|
||||
<div
|
||||
@ -358,40 +547,69 @@ const handlePause = async () => {
|
||||
</div>
|
||||
|
||||
{/* Fixed Timer at Bottom */}
|
||||
<div className="fixed bottom-36 left-0 right-0 bg-white/5 backdrop-blur-sm rounded-2xl p-6 border border-gray-200/50 shadow-lg glass-morphism z-30">
|
||||
<div className="fixed bottom-36 left-0 right-0 bg-white/95 backdrop-blur-sm border-t border-gray-200 px-4 sm:px-6 py-4 shadow-xl 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>
|
||||
<div
|
||||
className={`w-2 h-2 rounded-full ${
|
||||
isCompleted
|
||||
? "bg-green-400"
|
||||
: isResting
|
||||
? "bg-cyan-400 animate-pulse"
|
||||
: "bg-green-400 animate-pulse"
|
||||
}`}
|
||||
></div>
|
||||
<span className="text-sm font-bold text-gray-700">
|
||||
Подход {currentSet} из {totalSets}
|
||||
{isCompleted
|
||||
? `Завершено`
|
||||
: isResting
|
||||
? `Отдых перед подходом ${currentSet + 1}`
|
||||
: `Подход ${currentSet} из ${totalSets}`}
|
||||
</span>
|
||||
</div>
|
||||
<span className="text-sm font-black text-gray-700">
|
||||
{formatTime(currentTime)} / {formatTime(totalTime)}
|
||||
{isResting ? `${formatTime(restTime)} отдых` : `${formatTime(currentTime)} / ${formatTime(totalTime)}`}
|
||||
</span>
|
||||
</div>
|
||||
<div className="bg-white/30 rounded-full h-3 mb-4 overflow-hidden">
|
||||
<div className="bg-gray-200 rounded-full h-2 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"
|
||||
className={`h-2 rounded-full transition-all duration-1000 shadow-sm ${
|
||||
isCompleted
|
||||
? "bg-gradient-to-r from-green-400 via-green-500 to-green-600"
|
||||
: isResting
|
||||
? "bg-gradient-to-r from-cyan-400 via-cyan-500 to-cyan-600"
|
||||
: "bg-gradient-to-r from-[#2BACBE] via-cyan-500 to-cyan-700"
|
||||
}`}
|
||||
style={{ width: `${progress}%` }}
|
||||
></div>
|
||||
</div>
|
||||
<div className="flex space-x-3">
|
||||
{isCompleted ? (
|
||||
<div className="flex-1 font-bold py-3 px-4 rounded-xl bg-green-500 text-white flex items-center justify-center space-x-2">
|
||||
<CheckIcon />
|
||||
<span>Упражнение выполнено</span>
|
||||
</div>
|
||||
) : isResting ? (
|
||||
<div className="flex-1 font-bold py-3 px-4 rounded-xl bg-cyan-500 text-white flex items-center justify-center space-x-2">
|
||||
<RestIcon />
|
||||
<span>Отдых {formatTime(restTime)}</span>
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<button
|
||||
onClick={() => {
|
||||
if (!isPlaying) {
|
||||
setIsPlaying(true);
|
||||
setIsPlaying(true)
|
||||
} else {
|
||||
handlePause();
|
||||
setIsPlaying(false);
|
||||
handlePause()
|
||||
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
|
||||
? "bg-gray-400 text-white shadow-lg"
|
||||
: "bg-orange-400 text-white shadow-lg"
|
||||
}}
|
||||
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"
|
||||
}`}
|
||||
>
|
||||
{isPlaying ? (
|
||||
@ -400,32 +618,42 @@ const handlePause = async () => {
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<PlayIcon /> <span className="backdrop-blur-xl">Начать</span>
|
||||
<PlayIcon />
|
||||
<span className="backdrop-blur-xl">{hasSavedProgress ? "Продолжить" : "Начать"}</span>
|
||||
</>
|
||||
)}
|
||||
</button>
|
||||
|
||||
<button
|
||||
onClick={handleCompleteSet}
|
||||
className="px-6 py-3 bg-green-500 hover:bg-green-600 hover:scale-110 text-white font-bold rounded-xl transition-all duration-300 flex items-center justify-center"
|
||||
>
|
||||
<CheckIcon />
|
||||
</button>
|
||||
</>
|
||||
)}
|
||||
|
||||
<button
|
||||
onClick={() => {
|
||||
setCurrentTime(0)
|
||||
setIsPlaying(false)
|
||||
setHasSavedProgress(false)
|
||||
setIsCompleted(false)
|
||||
setCompletedSets([])
|
||||
setCurrentSet(1)
|
||||
setIsResting(false)
|
||||
setRestTime(0)
|
||||
handleClick()
|
||||
|
||||
localStorage.removeItem("exerciseProgress")
|
||||
}}
|
||||
className="cursor-pointer px-6 py-3 bg-white text-gray-800 font-bold rounded-xl transition-all duration-300 hover:scale-105 hover:shadow-lg border border-gray-200 flex items-center justify-center"
|
||||
|
||||
className="cursor-pointer px-6 py-3 bg-gray-300 hover:bg-gray-200 text-white font-bold rounded-xl transition-all duration-300 hover:shadow-lg hover:scale-110 flex items-center justify-center"
|
||||
>
|
||||
|
||||
<div
|
||||
onTransitionEnd={handleTransitionEnd}
|
||||
className="inline-block">
|
||||
<div onTransitionEnd={handleTransitionEnd} className="inline-block">
|
||||
<RefreshIcon
|
||||
className={`transition-transform duration-400 ease-in-out ${isRotating ? 'rotate-360' : ''}`}
|
||||
className={`transition-transform duration-400 ease-in-out ${isRotating ? "rotate-360" : ""}`}
|
||||
/>
|
||||
</div>
|
||||
|
||||
</button>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -27,12 +27,15 @@ export default function Home() {
|
||||
|
||||
const history = useHistory();
|
||||
const [currentDate, setCurrentDate] = useState("");
|
||||
const [error, setError] = useState<string>('');
|
||||
const [, setError] = useState<string>('');
|
||||
const [courses, setCourses] = useState<Course[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
const token = localStorage.getItem('authToken');
|
||||
|
||||
const exerciseProgress = localStorage.getItem('exerciseProgress');
|
||||
const currentProgress = localStorage.getItem('currentProgress')
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
console.log(token)
|
||||
@ -148,6 +151,8 @@ export default function Home() {
|
||||
<div className="flex content-center items-center justify-between ">
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
<div className="flex justify-between items-center">
|
||||
<div className="flex flex-col gap-6">
|
||||
@ -171,8 +176,10 @@ export default function Home() {
|
||||
</div>
|
||||
|
||||
<div className="px-4 sm:px-6 space-y-6">
|
||||
{/* Current Exercise */}
|
||||
<WorkoutCardHome onBackClick={handleBackClick} onCardClick={handleWorkoutClick} />
|
||||
{/* Текущее упражнение */}
|
||||
<WorkoutCardHome onBackClick={handleBackClick} onCardClick={handleWorkoutClick}
|
||||
/>
|
||||
|
||||
|
||||
{/* Quick Stats (Total Exercises & Total Courses) */}
|
||||
<div className="grid grid-cols-2 gap-4 md:gap-5">
|
||||
@ -191,6 +198,27 @@ export default function Home() {
|
||||
onClick={handleExercisesClick}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="bg-green-100">
|
||||
<p className="font-bold ">Взяли из локального хранилища:</p>
|
||||
надо считать прогресс исходя из выполненных/ вопрос: то есть, если выполнено, то как его исключить / как флаг проставлять?==
|
||||
все курсы мы вяли из ручки курсов, назначенные по роутингу (пути в router.ts)
|
||||
|
||||
надо достать текущий курс. который не выполнен.
|
||||
|
||||
как узнать, что курс выполнен?==
|
||||
|
||||
мы берем данные для текущей тренировки, которые записываются с помощью post, --оттуда достаем данные для текущего упражнения, которое записано (1 строка в БД всегда),
|
||||
а потом переходим на это упражнение.
|
||||
|
||||
и еще - мы считаем по индексу отображение, это же не повлияет на вывод итоговый на домашней странице, например?
|
||||
|
||||
|
||||
<div className="bg-red-200">
|
||||
<p>exerciseProgress:{exerciseProgress}</p>
|
||||
<p>currentProgress{currentProgress}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<BottomNavigation />
|
||||
|
Loading…
x
Reference in New Issue
Block a user