264 lines
9.7 KiB
TypeScript
264 lines
9.7 KiB
TypeScript
"use client"
|
||
|
||
import type React from "react"
|
||
import { useState, useEffect } from "react"
|
||
import {
|
||
IonContent,
|
||
IonPage,
|
||
IonCard,
|
||
IonCardContent,
|
||
IonCardHeader,
|
||
IonCardTitle,
|
||
IonButton,
|
||
IonIcon,
|
||
IonProgressBar,
|
||
IonBadge,
|
||
} from "@ionic/react"
|
||
import { playOutline, pauseOutline, stopOutline, timerOutline, fitnessOutline, trophyOutline } from "ionicons/icons"
|
||
|
||
interface Exercise {
|
||
id: number
|
||
name: string
|
||
duration: number // в секундах
|
||
description: string
|
||
difficulty: "easy" | "medium" | "hard"
|
||
completed: boolean
|
||
}
|
||
|
||
const Home: React.FC = () => {
|
||
const [currentExercise, setCurrentExercise] = useState<Exercise | null>(null)
|
||
const [timeLeft, setTimeLeft] = useState(0)
|
||
const [isRunning, setIsRunning] = useState(false)
|
||
const [isPaused, setIsPaused] = useState(false)
|
||
|
||
const exercises: Exercise[] = [
|
||
{
|
||
id: 1,
|
||
name: "Разминка плечевого сустава",
|
||
duration: 300, // 5 минут
|
||
description: "Медленные круговые движения плечами для восстановления подвижности",
|
||
difficulty: "easy",
|
||
completed: true,
|
||
},
|
||
{
|
||
id: 2,
|
||
name: "Упражнения для кисти",
|
||
duration: 600, // 10 минут
|
||
description: "Сжимание и разжимание кулака, вращения кистью",
|
||
difficulty: "medium",
|
||
completed: false,
|
||
},
|
||
{
|
||
id: 3,
|
||
name: "Растяжка мышц руки",
|
||
duration: 450, // 7.5 минут
|
||
description: "Статические упражнения на растяжку поврежденных мышц",
|
||
difficulty: "hard",
|
||
completed: false,
|
||
},
|
||
]
|
||
|
||
const [exerciseList, setExerciseList] = useState(exercises)
|
||
|
||
useEffect(() => {
|
||
let interval: NodeJS.Timeout
|
||
|
||
if (isRunning && !isPaused && timeLeft > 0) {
|
||
interval = setInterval(() => {
|
||
setTimeLeft(timeLeft - 1)
|
||
}, 1000)
|
||
} else if (timeLeft === 0 && currentExercise) {
|
||
// Упражнение завершено
|
||
setIsRunning(false)
|
||
setCurrentExercise(null)
|
||
// Отмечаем упражнение как выполненное
|
||
setExerciseList((prev) => prev.map((ex) => (ex.id === currentExercise.id ? { ...ex, completed: true } : ex)))
|
||
}
|
||
|
||
return () => clearInterval(interval)
|
||
}, [isRunning, isPaused, timeLeft, currentExercise])
|
||
|
||
const startExercise = (exercise: Exercise) => {
|
||
setCurrentExercise(exercise)
|
||
setTimeLeft(exercise.duration)
|
||
setIsRunning(true)
|
||
setIsPaused(false)
|
||
}
|
||
|
||
const pauseExercise = () => {
|
||
setIsPaused(!isPaused)
|
||
}
|
||
|
||
const stopExercise = () => {
|
||
setIsRunning(false)
|
||
setIsPaused(false)
|
||
setCurrentExercise(null)
|
||
setTimeLeft(0)
|
||
}
|
||
|
||
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 getDifficultyColor = (difficulty: string) => {
|
||
switch (difficulty) {
|
||
case "easy":
|
||
return "text-emerald-400"
|
||
case "medium":
|
||
return "text-yellow-400"
|
||
case "hard":
|
||
return "text-red-400"
|
||
default:
|
||
return "text-slate-400"
|
||
}
|
||
}
|
||
|
||
const getDifficultyBg = (difficulty: string) => {
|
||
switch (difficulty) {
|
||
case "easy":
|
||
return "bg-emerald-500/20 border-emerald-500/30"
|
||
case "medium":
|
||
return "bg-yellow-500/20 border-yellow-500/30"
|
||
case "hard":
|
||
return "bg-red-500/20 border-red-500/30"
|
||
default:
|
||
return "bg-slate-500/20 border-slate-500/30"
|
||
}
|
||
}
|
||
|
||
const completedCount = exerciseList.filter((ex) => ex.completed).length
|
||
const totalCount = exerciseList.length
|
||
const progressPercentage = (completedCount / totalCount) * 100
|
||
|
||
return (
|
||
<IonPage>
|
||
<IonContent className="bg-gradient-to-br from-slate-100 via-teal-50 to-cyan-50">
|
||
<div className="p-4 space-y-6">
|
||
{/* Приветствие и статистика */}
|
||
<div className="text-center mb-6">
|
||
<h1 className="text-2xl font-bold text-slate-800 mb-2">Добро пожаловать, Иван!</h1>
|
||
<p className="text-slate-600">Продолжайте восстановление. Вы на правильном пути!</p>
|
||
</div>
|
||
|
||
{/* Общий прогресс */}
|
||
<IonCard className="bg-white/80 backdrop-blur-sm border border-teal-200/50 shadow-lg">
|
||
<IonCardHeader>
|
||
<IonCardTitle className="text-slate-800 flex items-center gap-2">
|
||
<IonIcon icon={trophyOutline} className="text-teal-600" />
|
||
Общий прогресс
|
||
</IonCardTitle>
|
||
</IonCardHeader>
|
||
<IonCardContent>
|
||
<div className="space-y-4">
|
||
<div className="flex justify-between items-center">
|
||
<span className="text-slate-600">Выполнено упражнений</span>
|
||
<span className="font-bold text-teal-600">
|
||
{completedCount}/{totalCount}
|
||
</span>
|
||
</div>
|
||
<IonProgressBar value={progressPercentage / 100} className="h-2 rounded-full" color="success" />
|
||
<div className="text-center text-sm text-slate-500">{progressPercentage.toFixed(0)}% завершено</div>
|
||
</div>
|
||
</IonCardContent>
|
||
</IonCard>
|
||
|
||
{/* Текущее упражнение (если активно) */}
|
||
{currentExercise && (
|
||
<IonCard className="bg-gradient-to-r from-teal-500 to-cyan-500 text-white shadow-xl">
|
||
<IonCardHeader>
|
||
<IonCardTitle className="flex items-center gap-2">
|
||
<IonIcon icon={timerOutline} />
|
||
Текущее упражнение
|
||
</IonCardTitle>
|
||
</IonCardHeader>
|
||
<IonCardContent>
|
||
<div className="space-y-4">
|
||
<h3 className="text-lg font-semibold">{currentExercise.name}</h3>
|
||
<div className="text-center">
|
||
<div className="text-4xl font-bold mb-2">{formatTime(timeLeft)}</div>
|
||
<IonProgressBar
|
||
value={(currentExercise.duration - timeLeft) / currentExercise.duration}
|
||
className="h-2 rounded-full mb-4"
|
||
/>
|
||
</div>
|
||
<div className="flex gap-2 justify-center">
|
||
<IonButton fill="outline" color="light" onClick={pauseExercise}>
|
||
<IonIcon icon={isPaused ? playOutline : pauseOutline} slot="start" />
|
||
{isPaused ? "Продолжить" : "Пауза"}
|
||
</IonButton>
|
||
<IonButton fill="outline" color="light" onClick={stopExercise}>
|
||
<IonIcon icon={stopOutline} slot="start" />
|
||
Остановить
|
||
</IonButton>
|
||
</div>
|
||
</div>
|
||
</IonCardContent>
|
||
</IonCard>
|
||
)}
|
||
|
||
{/* Список упражнений */}
|
||
<div className="space-y-4">
|
||
<h2 className="text-xl font-bold text-slate-800 flex items-center gap-2">
|
||
<IonIcon icon={fitnessOutline} className="text-teal-600" />
|
||
Сегодняшние упражнения
|
||
</h2>
|
||
|
||
{exerciseList.map((exercise) => (
|
||
<IonCard
|
||
key={exercise.id}
|
||
className={`${exercise.completed ? "bg-emerald-50/80" : "bg-white/80"} backdrop-blur-sm border border-teal-200/50 shadow-md`}
|
||
>
|
||
<IonCardContent>
|
||
<div className="flex justify-between items-start mb-3">
|
||
<div className="flex-1">
|
||
<h3 className={`font-semibold ${exercise.completed ? "text-emerald-700" : "text-slate-800"}`}>
|
||
{exercise.name}
|
||
</h3>
|
||
<p className="text-sm text-slate-600 mt-1">{exercise.description}</p>
|
||
</div>
|
||
{exercise.completed && (
|
||
<IonBadge color="success" className="ml-2">
|
||
Выполнено
|
||
</IonBadge>
|
||
)}
|
||
</div>
|
||
|
||
<div className="flex justify-between items-center">
|
||
<div className="flex items-center gap-3">
|
||
<span className="text-sm text-slate-500">{formatTime(exercise.duration)}</span>
|
||
<IonBadge
|
||
className={`${getDifficultyBg(exercise.difficulty)} ${getDifficultyColor(exercise.difficulty)} border`}
|
||
>
|
||
{exercise.difficulty === "easy"
|
||
? "Легко"
|
||
: exercise.difficulty === "medium"
|
||
? "Средне"
|
||
: "Сложно"}
|
||
</IonBadge>
|
||
</div>
|
||
|
||
{!exercise.completed && !currentExercise && (
|
||
<IonButton
|
||
size="small"
|
||
onClick={() => startExercise(exercise)}
|
||
className="bg-gradient-to-r from-teal-500 to-cyan-500"
|
||
>
|
||
<IonIcon icon={playOutline} slot="start" />
|
||
Начать
|
||
</IonButton>
|
||
)}
|
||
</div>
|
||
</IonCardContent>
|
||
</IonCard>
|
||
))}
|
||
</div>
|
||
</div>
|
||
</IonContent>
|
||
</IonPage>
|
||
)
|
||
}
|
||
|
||
export default Home
|