Edit modals for Sets are complete

This commit is contained in:
AG
2025-12-12 00:12:18 +02:00
parent 70ea0a0ac3
commit 87f639e320
8 changed files with 379 additions and 385 deletions

View File

@@ -10,6 +10,8 @@ import { formatSetMetrics } from '../../utils/setFormatting';
import { useAuth } from '../../context/AuthContext';
import { api } from '../../services/api';
import RestTimerFAB from '../ui/RestTimerFAB';
import EditSetModal from '../EditSetModal';
interface ActiveSessionViewProps {
tracker: ReturnType<typeof useTracker>;
@@ -79,6 +81,15 @@ const ActiveSessionView: React.FC<ActiveSessionViewProps> = ({ tracker, activeSe
// Timer Logic is now managed in useTracker to persist across re-renders/step changes
const { timer } = tracker;
const [editingSet, setEditingSet] = React.useState<WorkoutSet | null>(null);
const handleSaveSetFromModal = async (updatedSet: WorkoutSet) => {
if (tracker.updateSet) {
tracker.updateSet(updatedSet);
}
setEditingSet(null);
};
const handleLogSet = async () => {
await handleAddSet();
@@ -244,145 +255,34 @@ const ActiveSessionView: React.FC<ActiveSessionViewProps> = ({ tracker, activeSe
<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)"
/>
)}
{(() => {
const exDef = exercises.find(e => e.name === set.exerciseName); // Best effort matching by name since set might not have exerciseId deeply populated in some contexts, but id is safer.
// Actually set has exerciseId usually. Let's try to match by ID if possible, else name.
// But wait, ActiveSession sets might not have exerciseId if created ad-hoc? No, they should.
// Let's assume we can look up by name if id missing, or just check set.side presence.
// Detailed look: The session object has sets.
// Ideally check exDef.isUnilateral.
const isUnilateral = set.side || (exercises.find(e => e.name === set.exerciseName)?.isUnilateral);
if (isUnilateral) {
return (
<div className="col-span-2 flex bg-surface-container-high rounded p-0.5">
{(['LEFT', 'ALTERNATELY', 'RIGHT'] as const).map((side) => {
const labelMap: Record<string, string> = { LEFT: 'L', RIGHT: 'R', ALTERNATELY: 'A' };
return (
<button
key={side}
onClick={() => setEditSide(side)}
title={t(side.toLowerCase() as any, lang)}
className={`flex-1 text-[10px] py-1 rounded transition-colors ${editSide === side
? 'bg-primary/10 text-primary font-bold'
: 'text-on-surface-variant hover:bg-surface-container'
}`}
>
{labelMap[side]}
</button>
);
})}
</div>
)
}
return null;
})()}
</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">
{formatSetMetrics(set, lang)}
</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">
{formatSetMetrics(set, lang)}
</div>
</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"
aria-label={t('cancel', lang)}
>
<X size={20} />
</button>
<button
onClick={() => handleSaveEdit(set)}
className="p-2 text-primary hover:bg-primary-container/20 rounded-full transition-colors"
aria-label={t('save', lang)}
>
<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"
aria-label={t('edit', lang)}
>
<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"
aria-label={t('delete', lang)}
>
<Trash2 size={20} />
</button>
</>
)}
<div className="flex items-center gap-1">
<button
onClick={() => setEditingSet(set)}
className="p-2 text-on-surface-variant hover:text-primary hover:bg-primary-container/20 rounded-full transition-colors"
aria-label={t('edit', lang)}
>
<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"
aria-label={t('delete', lang)}
>
<Trash2 size={20} />
</button>
</div>
</div>
);
@@ -456,6 +356,18 @@ const ActiveSessionView: React.FC<ActiveSessionViewProps> = ({ tracker, activeSe
</div>
)}
{/* Edit Set Modal */}
{editingSet && (
<EditSetModal
isOpen={!!editingSet}
onClose={() => setEditingSet(null)}
set={editingSet}
exerciseDef={tracker.exercises.find(e => e.id === editingSet.exerciseId) || tracker.exercises.find(e => e.name === editingSet.exerciseName)}
onSave={handleSaveSetFromModal}
lang={lang}
/>
)}
<RestTimerFAB timer={timer} onDurationChange={handleDurationChange} />
</div>
);