diff --git a/components/Plans.tsx b/components/Plans.tsx index 652b3a6..b06edc0 100644 --- a/components/Plans.tsx +++ b/components/Plans.tsx @@ -275,16 +275,19 @@ const Plans: React.FC = ({ userId, onStartPlan, lang }) => {
- {availableExercises.map(ex => ( - - ))} + {availableExercises + .slice() + .sort((a, b) => a.name.localeCompare(b.name)) + .map(ex => ( + + ))}
{isCreatingExercise && ( diff --git a/components/Tracker.tsx b/components/Tracker.tsx index f44c3cb..81de3a2 100644 --- a/components/Tracker.tsx +++ b/components/Tracker.tsx @@ -1,6 +1,6 @@ import React, { useState, useEffect } from 'react'; -import { Plus, Activity, ChevronDown, ChevronUp, Dumbbell, PlayCircle, CheckCircle, User, Scale, X, Flame, Timer as TimerIcon, ArrowUp, ArrowRight, Footprints, Ruler, CheckSquare, Trash2, Percent } from 'lucide-react'; +import { Plus, Activity, ChevronDown, ChevronUp, Dumbbell, PlayCircle, CheckCircle, User, Scale, X, Flame, Timer as TimerIcon, ArrowUp, ArrowRight, Footprints, Ruler, CheckSquare, Trash2, Percent, MoreVertical } from 'lucide-react'; import { WorkoutSession, WorkoutSet, ExerciseDef, ExerciseType, WorkoutPlan, Language } from '../types'; import { getExercises, getLastSetForExercise, saveExercise, getPlans } from '../services/storage'; import { getCurrentUserProfile } from '../services/auth'; @@ -49,6 +49,11 @@ const Tracker: React.FC = ({ userId, userWeight, activeSession, ac const [showPlanPrep, setShowPlanPrep] = useState(null); const [showPlanList, setShowPlanList] = useState(false); + // Confirmation State + const [showFinishConfirm, setShowFinishConfirm] = useState(false); + const [showQuitConfirm, setShowQuitConfirm] = useState(false); + const [showMenu, setShowMenu] = useState(false); + useEffect(() => { const loadData = async () => { const exList = await getExercises(userId); @@ -284,12 +289,40 @@ const Tracker: React.FC = ({ userId, userWeight, activeSession, ac {activeSession.userBodyWeight ? ` • ${activeSession.userBodyWeight}kg` : ''} - +
+ + + {showMenu && ( + <> +
setShowMenu(false)} + /> +
+ +
+ + )} +
{activePlan && ( @@ -348,9 +381,12 @@ const Tracker: React.FC = ({ userId, userWeight, activeSession, ac onChange={(e) => setSelectedExercise(exercises.find(ex => ex.id === e.target.value) || null)} > - {exercises.map(ex => ( - - ))} + {exercises + .slice() + .sort((a, b) => a.name.localeCompare(b.name)) + .map(ex => ( + + ))} @@ -497,6 +533,61 @@ const Tracker: React.FC = ({ userId, userWeight, activeSession, ac lang={lang} /> )} + + {/* Finish Confirmation Dialog */} + {showFinishConfirm && ( +
+
+

{t('finish_confirm_title', lang)}

+

{t('finish_confirm_msg', lang)}

+
+ + +
+
+
+ )} + + {/* Quit Without Saving Confirmation Dialog */} + {showQuitConfirm && ( +
+
+

{t('quit_confirm_title', lang)}

+

{t('quit_confirm_msg', lang)}

+
+ + +
+
+
+ )} ); }; diff --git a/server/prisma/dev.db b/server/prisma/dev.db index a981361..59d7a26 100644 Binary files a/server/prisma/dev.db and b/server/prisma/dev.db differ diff --git a/services/i18n.ts b/services/i18n.ts index 321b166..f765e9e 100644 --- a/services/i18n.ts +++ b/services/i18n.ts @@ -50,6 +50,12 @@ const translations = { cancel: 'Cancel', start: 'Start', finish: 'Finish', + finish_confirm_title: 'Finish Workout?', + finish_confirm_msg: 'Are you sure you want to finish this workout? Your progress will be saved.', + quit_no_save: 'Quit without saving', + quit_confirm_title: 'Quit without saving?', + quit_confirm_msg: 'All progress from this session will be lost. This action cannot be undone.', + confirm: 'Confirm', plan_completed: 'Plan Completed!', step: 'Step', of: 'of', @@ -71,7 +77,7 @@ const translations = { add_weight: 'Add. Weight', // Types - type_strength: 'Strength', + type_strength: 'Free Weights & Machines', type_bodyweight: 'Bodyweight', type_cardio: 'Cardio', type_static: 'Static', @@ -191,6 +197,12 @@ const translations = { cancel: 'Отмена', start: 'Начать', finish: 'Завершить', + finish_confirm_title: 'Завершить тренировку?', + finish_confirm_msg: 'Вы уверены, что хотите завершить эту тренировку? Ваш прогресс будет сохранен.', + quit_no_save: 'Выйти без сохранения', + quit_confirm_title: 'Выйти без сохранения?', + quit_confirm_msg: 'Весь прогресс этой сессии будет потерян. Это действие нельзя отменить.', + confirm: 'Подтвердить', plan_completed: 'План выполнен!', step: 'Шаг', of: 'из', @@ -212,7 +224,7 @@ const translations = { add_weight: 'Доп. вес', // Types - type_strength: 'Силовое', + type_strength: 'Свободные веса и тренажеры', type_bodyweight: 'Свой вес', type_cardio: 'Кардио', type_static: 'Статика',