327 lines
14 KiB
TypeScript
327 lines
14 KiB
TypeScript
"use client"
|
||
import type React from "react"
|
||
import { useState, useEffect } from "react"
|
||
import { useHistory } from "react-router-dom"
|
||
|
||
import BottomNavigation from "../components/BottomNavigation"
|
||
|
||
|
||
const Exercise: React.FC = () => {
|
||
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("/course-complete")
|
||
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
|
||
|
||
// SVG Icons
|
||
const BackIcon = () => (
|
||
<svg className="w-6 h-6 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 19l-7-7 7-7" />
|
||
</svg>
|
||
)
|
||
|
||
const HeartIcon = () => (
|
||
<svg className="w-6 h-6 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||
<path
|
||
strokeLinecap="round"
|
||
strokeLinejoin="round"
|
||
strokeWidth={2}
|
||
d="M4.318 6.318a4.5 4.5 0 000 6.364L12 20.364l7.682-7.682a4.5 4.5 0 00-6.364-6.364L12 7.636l-1.318-1.318a4.5 4.5 0 00-6.364 0z"
|
||
/>
|
||
</svg>
|
||
)
|
||
|
||
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:
|
||
"Лягте на спирку на коврик. Руки вдоль тела, ладони прижаты к полу. Ноги выпрямлены, носки направлены вверх. Поясница плотно прижата к полу.",
|
||
icon: "1",
|
||
},
|
||
{
|
||
title: "Задание",
|
||
description:
|
||
"Медленно поднимите прямые ноги до угла 90 градусов. Задержитесь на 2 секунды, затем медленно опустите ноги, не касаясь пола. Повторите движение плавно и контролируемо.",
|
||
icon: "2",
|
||
},
|
||
{
|
||
title: "Подходы",
|
||
description: "Выполните 3 подхода по 12 повторений с отдыхом 60 секунд между подходами.",
|
||
icon: "3",
|
||
},
|
||
{
|
||
title: "Перерыв",
|
||
description: "Отдыхайте 60 секунд между подходами. Дышите спокойно и расслабьте мышцы.",
|
||
icon: "4",
|
||
},
|
||
{
|
||
title: "Динамика и статика",
|
||
description:
|
||
"Динамическая фаза: подъем и опускание ног выполняется плавно, 2 секунды вверх, 2 секунды вниз. Статическая фаза: удержание ног в верхней точке на 2 секунды.",
|
||
icon: "5",
|
||
},
|
||
]
|
||
|
||
return (
|
||
|
||
<div className="bg-gray-100 max-w-4xl mx-auto mb-50 overflow_auto">
|
||
|
||
|
||
{/* Header */}
|
||
<div className="fixed top-0 left-0 right-0 bg-gradient-to-br from-[#3ABBC7] to-[#0D212C] p-4 z-50 shadow-lg rounded-b-3xl">
|
||
<div className="absolute inset-0 bg-gradient-to-r from-black/10 to-transparent"></div>
|
||
|
||
<div className="relative px-4 sm:px-6 py-4 max-w-4xl mx-auto flex flex-row justify-between">
|
||
|
||
|
||
<button
|
||
onClick={() => history.goBack()}
|
||
className="w-12 h-12 glass-morphism rounded-2xl flex items-center justify-center border border-white/30 hover:bg-white/30 transition-all duration-300 shadow-lg"
|
||
>
|
||
<svg className="w-6 h-6 text-white transform rotate-180" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 19l-7-7L15 5" />
|
||
</svg>
|
||
</button>
|
||
<div className="text-center">
|
||
|
||
<h1 className="text-xl sm:text-2xl font-semibold text-white">Подъемы ног лежа</h1>
|
||
<p className="text-cyan-100 text-sm font-medium">Упражнение 1 из 12</p>
|
||
</div>
|
||
|
||
<button
|
||
onClick={() => history.goBack()}
|
||
className="w-12 h-12 glass-morphism rounded-2xl flex items-center justify-center border border-white/30 hover:bg-white/30 transition-all duration-300 shadow-lg ml-auto"
|
||
>
|
||
<svg className="w-6 h-6 text-white transform rotate-180" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 19l-7-7L15 5" />
|
||
</svg>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
|
||
|
||
|
||
{/* Header */}
|
||
<div className="sticky top-0 z-40 bg-gradient-to-br from-[#3ABBC7] to-[#0D212C] backdrop-blur-xl pt-40 pb-4 px-4 sm:px-6">
|
||
<div className="flex items-center justify-between mb-4">
|
||
<button
|
||
onClick={() => history.goBack()}
|
||
className="w-12 h-12 glass-morphism rounded-2xl flex items-center justify-center border border-white/30 hover:bg-white/30 transition-all duration-300 shadow-lg"
|
||
>
|
||
<BackIcon />
|
||
</button>
|
||
<div className="text-center">
|
||
<h1 className="text-lg sm:text-xl font-black text-white">Подъемы ног лежа</h1>
|
||
<p className="text-white/80 text-sm font-medium">Упражнение 1 из 12</p>
|
||
</div>
|
||
<div className="w-12 h-12 glass-morphism rounded-2xl flex items-center justify-center border border-white/30 shadow-lg">
|
||
<HeartIcon />
|
||
</div>
|
||
</div>
|
||
{/* Set Counter */}
|
||
<div className="flex justify-center space-x-2 mt-4">
|
||
{Array.from({ length: totalSets }).map((_, index) => (
|
||
<div
|
||
key={index}
|
||
className={`w-8 h-8 rounded-full flex items-center justify-center font-bold text-sm transition-all duration-300 ${index + 1 <= currentSet
|
||
? "bg-white text-[#2BACBE] shadow-lg scale-110 ring-2 ring-white"
|
||
: "bg-gray-100 text-gray-600 border border-gray-200"
|
||
}`}
|
||
>
|
||
{index + 1}
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
|
||
|
||
{/* Video/Image Section */}
|
||
<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="/placeholder.svg?height=250&width=400"
|
||
alt="Exercise demonstration"
|
||
className="w-full h-56 sm:h-64 object-cover"
|
||
/>
|
||
<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="w-20 h-20 bg-white/90 backdrop-blur-xl rounded-full flex items-center justify-center shadow-2xl hover:bg-white transition-all duration-300 transform hover:scale-110 border border-white/50"
|
||
>
|
||
{isPlaying ? <PauseIcon /> : <PlayIcon />}
|
||
</button>
|
||
</div>
|
||
{/* Live indicators */}
|
||
{isPlaying && (
|
||
<div className="absolute top-4 left-4 flex items-center space-x-2">
|
||
<div className="w-3 h-3 bg-red-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">
|
||
LIVE
|
||
</span>
|
||
</div>
|
||
)}
|
||
{/* Timer Display */}
|
||
<div className="absolute top-4 right-4 bg-white/90 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>
|
||
{/* Exercise Steps - Vertical Scroll */}
|
||
<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
|
||
className={`w-12 h-12 bg-gray-100 rounded-xl flex items-center justify-center shadow-sm flex-shrink-0 text-[#2BACBE] font-black text-xl border border-gray-200`}
|
||
>
|
||
{step.icon}
|
||
</div>
|
||
<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>
|
||
{/* Important Notes */}
|
||
<div className="px-4 sm:px-6 mb-32">
|
||
<div className="bg-white rounded-2xl p-5 border border-gray-200 shadow-lg">
|
||
<div className="flex items-start space-x-3">
|
||
<div className="w-8 h-8 bg-amber-400/20 rounded-full flex items-center justify-center flex-shrink-0">
|
||
<span className="text-amber-400 font-bold text-sm">!</span>
|
||
</div>
|
||
<div>
|
||
<h3 className="text-lg font-black text-gray-800 mb-2">Важные замечания</h3>
|
||
<ul className="space-y-1 text-gray-600 text-sm">
|
||
<li>• Не отрывайте поясницу от пола</li>
|
||
<li>• Дышите равномерно, не задерживайте дыхание</li>
|
||
<li>• При болевых ощущениях немедленно прекратите</li>
|
||
<li>• Движения должны быть плавными и контролируемыми</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
{/* Fixed Timer at Bottom */}
|
||
<div className="fixed bottom-36 left-0 right-0 glass-morphism 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>Начать</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>
|
||
)
|
||
}
|
||
|
||
export default Exercise
|