Critical Stability & Performance fixes. Excessive Log Set button gone on QIuck Log screen

This commit is contained in:
AG
2025-12-06 08:58:44 +02:00
parent 27afacee3f
commit 1c3e15516c
35 changed files with 48 additions and 26 deletions

View File

@@ -0,0 +1,383 @@
import React from 'react';
import { MoreVertical, X, CheckSquare, ChevronUp, ChevronDown, Scale, Dumbbell, Plus, Activity, Timer as TimerIcon, ArrowRight, ArrowUp, CheckCircle, Edit, Trash2 } from 'lucide-react';
import { ExerciseType, Language, WorkoutSet } from '../../types';
import { t } from '../../services/i18n';
import FilledInput from '../FilledInput';
import ExerciseModal from '../ExerciseModal';
import { useTracker } from './useTracker';
import SetLogger from './SetLogger';
interface ActiveSessionViewProps {
tracker: ReturnType<typeof useTracker>;
activeSession: any; // Using any to avoid strict type issues with the complex session object for now, but ideally should be WorkoutSession
lang: Language;
onSessionEnd: () => void;
onSessionQuit: () => void;
onRemoveSet: (setId: string) => void;
}
const ActiveSessionView: React.FC<ActiveSessionViewProps> = ({ tracker, activeSession, lang, onSessionEnd, onSessionQuit, onRemoveSet }) => {
const {
elapsedTime,
showFinishConfirm,
setShowFinishConfirm,
showQuitConfirm,
setShowQuitConfirm,
showMenu,
setShowMenu,
activePlan, // This comes from useTracker props but we might need to pass it explicitly if not in hook return
currentStepIndex,
showPlanList,
setShowPlanList,
jumpToStep,
searchQuery,
setSearchQuery,
setShowSuggestions,
showSuggestions,
filteredExercises,
setSelectedExercise,
selectedExercise,
weight,
setWeight,
reps,
setReps,
duration,
setDuration,
distance,
setDistance,
height,
setHeight,
handleAddSet,
editingSetId,
editWeight,
setEditWeight,
editReps,
setEditReps,
editDuration,
setEditDuration,
editDistance,
setEditDistance,
editHeight,
setEditHeight,
handleCancelEdit,
handleSaveEdit,
handleEditSet,
isCreating,
setIsCreating,
handleCreateExercise,
exercises
} = tracker;
const isPlanFinished = activePlan && currentStepIndex >= activePlan.steps.length;
return (
<div className="flex flex-col h-full max-h-full overflow-hidden relative bg-surface">
<div className="px-4 py-3 bg-surface-container shadow-elevation-1 z-20 flex justify-between items-center">
<div className="flex flex-col">
<h2 className="text-title-medium text-on-surface flex items-center gap-2 font-medium">
<span className="w-2 h-2 rounded-full bg-error animate-pulse" />
{activePlan ? activePlan.name : t('free_workout', lang)}
</h2>
<span className="text-xs text-on-surface-variant font-mono mt-0.5 flex items-center gap-2">
<span className="bg-surface-container-high px-1.5 py-0.5 rounded text-on-surface font-bold">{elapsedTime}</span>
{activeSession.userBodyWeight ? `${activeSession.userBodyWeight}kg` : ''}
</span>
</div>
<div className="flex items-center gap-2 relative">
<button
onClick={() => setShowFinishConfirm(true)}
className="px-5 py-2 rounded-full bg-error-container text-on-error-container text-sm font-medium hover:opacity-90 transition-opacity"
>
{t('finish', lang)}
</button>
<button
onClick={() => setShowMenu(!showMenu)}
className="p-2 rounded-full bg-surface-container-high text-on-surface hover:bg-surface-container-highest transition-colors"
>
<MoreVertical size={20} />
</button>
{showMenu && (
<>
<div
className="fixed inset-0 z-30"
onClick={() => setShowMenu(false)}
/>
<div className="absolute right-0 top-full mt-2 bg-surface-container rounded-xl shadow-elevation-3 overflow-hidden z-40 min-w-[200px]">
<button
onClick={() => {
setShowMenu(false);
setShowQuitConfirm(true);
}}
className="w-full px-4 py-3 text-left text-error hover:bg-error-container/20 transition-colors flex items-center gap-2"
>
<X size={18} />
{t('quit_no_save', lang)}
</button>
</div>
</>
)}
</div>
</div>
{activePlan && (
<div className="bg-surface-container-low border-b border-outline-variant">
<button
onClick={() => setShowPlanList(!showPlanList)}
className="w-full px-4 py-3 flex justify-between items-center"
>
<div className="flex flex-col items-start">
{isPlanFinished ? (
<div className="flex items-center gap-2 text-primary">
<CheckSquare size={18} />
<span className="font-bold">{t('plan_completed', lang)}</span>
</div>
) : (
<>
<span className="text-[10px] text-primary font-medium tracking-wider">{t('step', lang)} {currentStepIndex + 1} {t('of', lang)} {activePlan.steps.length}</span>
<div className="font-medium text-on-surface flex items-center gap-2">
{activePlan.steps[currentStepIndex].exerciseName}
{activePlan.steps[currentStepIndex].isWeighted && <Scale size={12} className="text-primary" />}
</div>
</>
)}
</div>
{showPlanList ? <ChevronUp size={20} className="text-on-surface-variant" /> : <ChevronDown size={20} className="text-on-surface-variant" />}
</button>
{showPlanList && (
<div className="max-h-48 overflow-y-auto bg-surface-container-high p-2 space-y-1 animate-in slide-in-from-top-2">
{activePlan.steps.map((step, idx) => (
<button
key={step.id}
onClick={() => jumpToStep(idx)}
className={`w-full text-left px-4 py-3 rounded-full text-sm flex items-center justify-between transition-colors ${idx === currentStepIndex
? 'bg-primary-container text-on-primary-container font-medium'
: idx < currentStepIndex
? 'text-on-surface-variant opacity-50'
: 'text-on-surface hover:bg-white/5'
}`}
>
<span>{idx + 1}. {step.exerciseName}</span>
{step.isWeighted && <Scale size={14} />}
</button>
))}
</div>
)}
</div>
)}
<div className="flex-1 overflow-y-auto p-4 pb-32 space-y-6">
<SetLogger
tracker={tracker}
lang={lang}
onLogSet={handleAddSet}
/>
{activeSession.sets.length > 0 && (
<div className="pt-4">
<h3 className="text-sm text-primary font-medium px-2 mb-3 tracking-wide">{t('history_section', lang)}</h3>
<div className="flex flex-col gap-2">
{[...activeSession.sets].reverse().map((set: WorkoutSet, idx: number) => {
const setNumber = activeSession.sets.length - idx;
const isEditing = editingSetId === set.id;
return (
<div key={set.id} className="flex justify-between items-center p-4 bg-surface-container rounded-xl shadow-elevation-1 animate-in fade-in slide-in-from-bottom-2">
<div className="flex items-center gap-4 flex-1">
<div className="w-8 h-8 rounded-full bg-secondary-container text-on-secondary-container flex items-center justify-center text-xs font-bold">
{setNumber}
</div>
{isEditing ? (
<div className="flex-1">
<div className="text-base font-medium text-on-surface mb-2">{set.exerciseName}{set.side && <span className="ml-2 text-xs font-medium text-on-surface-variant">{t(set.side.toLowerCase() as any, lang)}</span>}</div>
<div className="grid grid-cols-2 gap-2">
{set.weight !== undefined && (
<input
type="number"
step="0.1"
value={editWeight}
onChange={(e) => setEditWeight(e.target.value)}
className="px-2 py-1 bg-surface-container-high rounded text-sm text-on-surface border border-outline-variant focus:border-primary focus:outline-none"
placeholder="Weight (kg)"
/>
)}
{set.reps !== undefined && (
<input
type="number"
value={editReps}
onChange={(e) => setEditReps(e.target.value)}
className="px-2 py-1 bg-surface-container-high rounded text-sm text-on-surface border border-outline-variant focus:border-primary focus:outline-none"
placeholder="Reps"
/>
)}
{set.durationSeconds !== undefined && (
<input
type="number"
value={editDuration}
onChange={(e) => setEditDuration(e.target.value)}
className="px-2 py-1 bg-surface-container-high rounded text-sm text-on-surface border border-outline-variant focus:border-primary focus:outline-none"
placeholder="Duration (s)"
/>
)}
{set.distanceMeters !== undefined && (
<input
type="number"
step="0.1"
value={editDistance}
onChange={(e) => setEditDistance(e.target.value)}
className="px-2 py-1 bg-surface-container-high rounded text-sm text-on-surface border border-outline-variant focus:border-primary focus:outline-none"
placeholder="Distance (m)"
/>
)}
{set.height !== undefined && (
<input
type="number"
step="0.1"
value={editHeight}
onChange={(e) => setEditHeight(e.target.value)}
className="px-2 py-1 bg-surface-container-high rounded text-sm text-on-surface border border-outline-variant focus:border-primary focus:outline-none"
placeholder="Height (cm)"
/>
)}
</div>
</div>
) : (
<div>
<div className="text-base font-medium text-on-surface">{set.exerciseName}{set.side && <span className="ml-2 text-xs font-medium text-on-surface-variant">{t(set.side.toLowerCase() as any, lang)}</span>}</div>
<div className="text-sm text-on-surface-variant">
{set.type === ExerciseType.STRENGTH &&
`${set.weight ? `${set.weight}kg` : ''} ${set.reps ? `x ${set.reps}` : ''}`.trim()
}
{set.type === ExerciseType.BODYWEIGHT &&
`${set.weight ? `${set.weight}kg` : ''} ${set.reps ? `x ${set.reps}` : ''}`.trim()
}
{set.type === ExerciseType.CARDIO &&
`${set.durationSeconds ? `${set.durationSeconds}s` : ''} ${set.distanceMeters ? `/ ${set.distanceMeters}m` : ''}`.trim()
}
{set.type === ExerciseType.STATIC &&
`${set.durationSeconds ? `${set.durationSeconds}s` : ''}`.trim()
}
{set.type === ExerciseType.HIGH_JUMP &&
`${set.height ? `${set.height}cm` : ''}`.trim()
}
{set.type === ExerciseType.LONG_JUMP &&
`${set.distanceMeters ? `${set.distanceMeters}m` : ''}`.trim()
}
{set.type === ExerciseType.PLYOMETRIC &&
`${set.reps ? `x ${set.reps}` : ''}`.trim()
}
</div>
</div>
)}
</div>
<div className="flex items-center gap-2">
{isEditing ? (
<>
<button
onClick={handleCancelEdit}
className="p-2 text-on-surface-variant hover:text-on-surface hover:bg-surface-container-high rounded-full transition-colors"
>
<X size={20} />
</button>
<button
onClick={() => handleSaveEdit(set)}
className="p-2 text-primary hover:bg-primary-container/20 rounded-full transition-colors"
>
<CheckCircle size={20} />
</button>
</>
) : (
<>
<button
onClick={() => handleEditSet(set)}
className="p-2 text-on-surface-variant hover:text-primary hover:bg-primary-container/20 rounded-full transition-colors"
>
<Edit size={20} />
</button>
<button
onClick={() => onRemoveSet(set.id)}
className="p-2 text-on-surface-variant hover:text-error hover:bg-error-container/10 rounded-full transition-colors"
>
<Trash2 size={20} />
</button>
</>
)}
</div>
</div>
);
})}
</div>
</div>
)}
</div>
{isCreating && (
<ExerciseModal
isOpen={isCreating}
onClose={() => setIsCreating(false)}
onSave={handleCreateExercise}
lang={lang}
existingExercises={exercises}
/>
)}
{/* Finish Confirmation Dialog */}
{showFinishConfirm && (
<div className="fixed inset-0 bg-black/60 z-50 flex items-center justify-center p-6 backdrop-blur-sm">
<div className="w-full max-w-sm bg-surface-container rounded-[28px] p-6 shadow-elevation-3">
<h3 className="text-2xl font-normal text-on-surface mb-2">{t('finish_confirm_title', lang)}</h3>
<p className="text-on-surface-variant text-sm mb-8">{t('finish_confirm_msg', lang)}</p>
<div className="flex justify-end gap-2">
<button
onClick={() => setShowFinishConfirm(false)}
className="px-6 py-2.5 rounded-full text-primary font-medium hover:bg-white/5"
>
{t('cancel', lang)}
</button>
<button
onClick={() => {
setShowFinishConfirm(false);
onSessionEnd();
}}
className="px-6 py-2.5 rounded-full bg-green-600 text-white font-medium hover:bg-green-700"
>
{t('confirm', lang)}
</button>
</div>
</div>
</div>
)}
{/* Quit Without Saving Confirmation Dialog */}
{showQuitConfirm && (
<div className="fixed inset-0 bg-black/60 z-50 flex items-center justify-center p-6 backdrop-blur-sm">
<div className="w-full max-w-sm bg-surface-container rounded-[28px] p-6 shadow-elevation-3">
<h3 className="text-2xl font-normal text-error mb-2">{t('quit_confirm_title', lang)}</h3>
<p className="text-on-surface-variant text-sm mb-8">{t('quit_confirm_msg', lang)}</p>
<div className="flex justify-end gap-2">
<button
onClick={() => setShowQuitConfirm(false)}
className="px-6 py-2.5 rounded-full text-primary font-medium hover:bg-white/5"
>
{t('cancel', lang)}
</button>
<button
onClick={() => {
setShowQuitConfirm(false);
onSessionQuit();
}}
className="px-6 py-2.5 rounded-full bg-green-600 text-white font-medium hover:bg-green-700"
>
{t('confirm', lang)}
</button>
</div>
</div>
</div>
)}
</div>
);
};
export default ActiveSessionView;