1. Removed Prev. and Body Weight % elements from the session logging screen. 2. Added logged set editing.
This commit is contained in:
13
App.tsx
13
App.tsx
@@ -134,6 +134,18 @@ function App() {
|
||||
}
|
||||
};
|
||||
|
||||
const handleUpdateSetInActive = (updatedSet: WorkoutSet) => {
|
||||
if (activeSession) {
|
||||
setActiveSession(prev => {
|
||||
if (!prev) return null;
|
||||
return {
|
||||
...prev,
|
||||
sets: prev.sets.map(s => s.id === updatedSet.id ? updatedSet : s)
|
||||
};
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const handleUpdateSession = (updatedSession: WorkoutSession) => {
|
||||
if (!currentUser) return;
|
||||
saveSession(currentUser.id, updatedSession);
|
||||
@@ -169,6 +181,7 @@ function App() {
|
||||
onSessionEnd={handleEndSession}
|
||||
onSetAdded={handleAddSet}
|
||||
onRemoveSet={handleRemoveSetFromActive}
|
||||
onUpdateSet={handleUpdateSetInActive}
|
||||
lang={language}
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -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, MoreVertical } 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, Edit } 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';
|
||||
@@ -15,13 +15,14 @@ interface TrackerProps {
|
||||
onSessionEnd: () => void;
|
||||
onSetAdded: (set: WorkoutSet) => void;
|
||||
onRemoveSet: (setId: string) => void;
|
||||
onUpdateSet: (set: WorkoutSet) => void;
|
||||
lang: Language;
|
||||
}
|
||||
|
||||
import FilledInput from './FilledInput';
|
||||
import ExerciseModal from './ExerciseModal';
|
||||
|
||||
const Tracker: React.FC<TrackerProps> = ({ userId, userWeight, activeSession, activePlan, onSessionStart, onSessionEnd, onSetAdded, onRemoveSet, lang }) => {
|
||||
const Tracker: React.FC<TrackerProps> = ({ userId, userWeight, activeSession, activePlan, onSessionStart, onSessionEnd, onSetAdded, onRemoveSet, onUpdateSet, lang }) => {
|
||||
const [exercises, setExercises] = useState<ExerciseDef[]>([]);
|
||||
const [plans, setPlans] = useState<WorkoutPlan[]>([]);
|
||||
const [selectedExercise, setSelectedExercise] = useState<ExerciseDef | null>(null);
|
||||
@@ -54,6 +55,14 @@ const Tracker: React.FC<TrackerProps> = ({ userId, userWeight, activeSession, ac
|
||||
const [showQuitConfirm, setShowQuitConfirm] = useState(false);
|
||||
const [showMenu, setShowMenu] = useState(false);
|
||||
|
||||
// Edit Set State
|
||||
const [editingSetId, setEditingSetId] = useState<string | null>(null);
|
||||
const [editWeight, setEditWeight] = useState<string>('');
|
||||
const [editReps, setEditReps] = useState<string>('');
|
||||
const [editDuration, setEditDuration] = useState<string>('');
|
||||
const [editDistance, setEditDistance] = useState<string>('');
|
||||
const [editHeight, setEditHeight] = useState<string>('');
|
||||
|
||||
useEffect(() => {
|
||||
const loadData = async () => {
|
||||
const exList = await getExercises(userId);
|
||||
@@ -174,6 +183,32 @@ const Tracker: React.FC<TrackerProps> = ({ userId, userWeight, activeSession, ac
|
||||
setIsCreating(false);
|
||||
};
|
||||
|
||||
const handleEditSet = (set: WorkoutSet) => {
|
||||
setEditingSetId(set.id);
|
||||
setEditWeight(set.weight?.toString() || '');
|
||||
setEditReps(set.reps?.toString() || '');
|
||||
setEditDuration(set.durationSeconds?.toString() || '');
|
||||
setEditDistance(set.distanceMeters?.toString() || '');
|
||||
setEditHeight(set.height?.toString() || '');
|
||||
};
|
||||
|
||||
const handleSaveEdit = (set: WorkoutSet) => {
|
||||
const updatedSet: WorkoutSet = {
|
||||
...set,
|
||||
...(editWeight && { weight: parseFloat(editWeight) }),
|
||||
...(editReps && { reps: parseInt(editReps) }),
|
||||
...(editDuration && { durationSeconds: parseInt(editDuration) }),
|
||||
...(editDistance && { distanceMeters: parseFloat(editDistance) }),
|
||||
...(editHeight && { height: parseFloat(editHeight) })
|
||||
};
|
||||
onUpdateSet(updatedSet);
|
||||
setEditingSetId(null);
|
||||
};
|
||||
|
||||
const handleCancelEdit = () => {
|
||||
setEditingSetId(null);
|
||||
};
|
||||
|
||||
const jumpToStep = (index: number) => {
|
||||
if (!activePlan) return;
|
||||
setCurrentStepIndex(index);
|
||||
@@ -446,22 +481,6 @@ const Tracker: React.FC<TrackerProps> = ({ userId, userWeight, activeSession, ac
|
||||
)}
|
||||
</div>
|
||||
|
||||
{(selectedExercise.type === ExerciseType.BODYWEIGHT || selectedExercise.type === ExerciseType.STATIC) && (
|
||||
<div className="flex items-center gap-4 px-2">
|
||||
<div className="flex items-center gap-2 text-on-surface-variant">
|
||||
<Percent size={16} />
|
||||
<span className="text-xs font-medium">{t('body_weight_percent', lang)}</span>
|
||||
</div>
|
||||
<input
|
||||
type="number"
|
||||
className="w-20 border-b border-outline-variant bg-transparent text-center text-on-surface focus:border-primary focus:outline-none"
|
||||
value={bwPercentage}
|
||||
onChange={(e) => setBwPercentage(e.target.value)}
|
||||
/>
|
||||
<span className="text-on-surface-variant text-sm">%</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<button
|
||||
onClick={handleAddSet}
|
||||
className="w-full h-14 bg-primary-container text-on-primary-container font-medium text-lg rounded-full shadow-elevation-2 hover:shadow-elevation-3 active:scale-[0.98] transition-all flex items-center justify-center gap-2"
|
||||
@@ -469,22 +488,6 @@ const Tracker: React.FC<TrackerProps> = ({ userId, userWeight, activeSession, ac
|
||||
<CheckCircle size={24} />
|
||||
<span>{t('log_set', lang)}</span>
|
||||
</button>
|
||||
|
||||
<div className="flex justify-center">
|
||||
<div className="bg-surface-container px-4 py-2 rounded-full border border-outline-variant/20 text-xs text-on-surface-variant">
|
||||
{t('prev', lang)}: <span className="text-on-surface font-medium ml-1">
|
||||
{lastSet ? (
|
||||
<>
|
||||
{lastSet?.weight ? `${lastSet?.weight}kg × ` : ''}
|
||||
{lastSet?.reps ? `${lastSet?.reps}` : ''}
|
||||
{lastSet?.distanceMeters ? `${lastSet?.distanceMeters}m` : ''}
|
||||
{lastSet?.height ? `${lastSet?.height}cm` : ''}
|
||||
{lastSet?.durationSeconds ? `${lastSet?.durationSeconds}s` : ''}
|
||||
</>
|
||||
) : '—'}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -494,12 +497,68 @@ const Tracker: React.FC<TrackerProps> = ({ userId, userWeight, activeSession, ac
|
||||
<div className="flex flex-col gap-2">
|
||||
{[...activeSession.sets].reverse().map((set, idx) => {
|
||||
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">
|
||||
<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}</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}</div>
|
||||
<div className="text-sm text-on-surface-variant">
|
||||
@@ -510,13 +569,41 @@ const Tracker: React.FC<TrackerProps> = ({ userId, userWeight, activeSession, ac
|
||||
{set.height !== undefined && `${set.height}cm`}
|
||||
</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>
|
||||
);
|
||||
})}
|
||||
@@ -552,7 +639,7 @@ const Tracker: React.FC<TrackerProps> = ({ userId, userWeight, activeSession, ac
|
||||
setShowFinishConfirm(false);
|
||||
onSessionEnd();
|
||||
}}
|
||||
className="px-6 py-2.5 rounded-full bg-primary text-on-primary font-medium"
|
||||
className="px-6 py-2.5 rounded-full bg-green-600 text-white font-medium hover:bg-green-700"
|
||||
>
|
||||
{t('confirm', lang)}
|
||||
</button>
|
||||
@@ -580,7 +667,7 @@ const Tracker: React.FC<TrackerProps> = ({ userId, userWeight, activeSession, ac
|
||||
// Quit without saving - just navigate away or reset
|
||||
window.location.reload();
|
||||
}}
|
||||
className="px-6 py-2.5 rounded-full bg-error text-on-error font-medium"
|
||||
className="px-6 py-2.5 rounded-full bg-green-600 text-white font-medium hover:bg-green-700"
|
||||
>
|
||||
{t('confirm', lang)}
|
||||
</button>
|
||||
|
||||
Binary file not shown.
Reference in New Issue
Block a user