diff --git a/components/ExerciseModal.tsx b/components/ExerciseModal.tsx index 67896ed..3d62720 100644 --- a/components/ExerciseModal.tsx +++ b/components/ExerciseModal.tsx @@ -1,4 +1,5 @@ import React, { useState } from 'react'; +import { toTitleCase } from '../utils/text'; import { X, Dumbbell, User, Flame, Timer as TimerIcon, ArrowUp, ArrowRight, Footprints, Ruler, Percent } from 'lucide-react'; import { ExerciseDef, ExerciseType, Language } from '../types'; import { t } from '../services/i18n'; @@ -82,6 +83,7 @@ const ExerciseModal: React.FC = ({ isOpen, onClose, onSave, type="text" autoFocus autocapitalize="words" + onBlur={() => setNewName(toTitleCase(newName))} /> {error && (

{error}

diff --git a/components/FilledInput.tsx b/components/FilledInput.tsx index d363c80..8544b64 100644 --- a/components/FilledInput.tsx +++ b/components/FilledInput.tsx @@ -1,9 +1,11 @@ import React from 'react'; +import { X } from 'lucide-react'; interface FilledInputProps { label: string; value: string | number; onChange: (e: React.ChangeEvent) => void; + onClear?: () => void; onFocus?: (e: React.FocusEvent) => void; onBlur?: (e: React.FocusEvent) => void; type?: string; @@ -13,28 +15,53 @@ interface FilledInputProps { inputMode?: "search" | "text" | "email" | "tel" | "url" | "none" | "numeric" | "decimal"; autocapitalize?: "off" | "none" | "on" | "sentences" | "words" | "characters"; autoComplete?: string; + rightElement?: React.ReactNode; } -const FilledInput: React.FC = ({ label, value, onChange, onFocus, onBlur, type = "number", icon, autoFocus, step, inputMode, autocapitalize, autoComplete }) => ( -
- - -
-); +const FilledInput: React.FC = ({ label, value, onChange, onClear, onFocus, onBlur, type = "number", icon, autoFocus, step, inputMode, autocapitalize, autoComplete, rightElement }) => { + const handleClear = () => { + const syntheticEvent = { + target: { value: '' } + } as React.ChangeEvent; + onChange(syntheticEvent); + if (onClear) onClear(); + }; + + return ( +
+ + + {value !== '' && value !== 0 && ( + + )} + {rightElement && ( +
+ {rightElement} +
+ )} +
+ ); +}; export default FilledInput; diff --git a/components/History.tsx b/components/History.tsx index ba6c57a..94ddd10 100644 --- a/components/History.tsx +++ b/components/History.tsx @@ -14,6 +14,7 @@ interface HistoryProps { const History: React.FC = ({ sessions, onUpdateSession, onDeleteSession, lang }) => { const [editingSession, setEditingSession] = useState(null); const [deletingId, setDeletingId] = useState(null); + const [deletingSetInfo, setDeletingSetInfo] = useState<{ sessionId: string, setId: string } | null>(null); const calculateSessionWork = (session: WorkoutSession) => { @@ -86,6 +87,18 @@ const History: React.FC = ({ sessions, onUpdateSession, onDeleteSe if (deletingId && onDeleteSession) { onDeleteSession(deletingId); setDeletingId(null); + } else if (deletingSetInfo && onUpdateSession) { + // Find the session + const session = sessions.find(s => s.id === deletingSetInfo.sessionId); + if (session) { + // Create updated session with the set removed + const updatedSession = { + ...session, + sets: session.sets.filter(s => s.id !== deletingSetInfo.setId) + }; + onUpdateSession(updatedSession); + } + setDeletingSetInfo(null); } } @@ -238,8 +251,7 @@ const History: React.FC = ({ sessions, onUpdateSession, onDeleteSe // Find the session and set up for deletion const parentSession = daySessions.find(s => s.sets.some(st => st.id === set.id)); if (parentSession) { - setEditingSession(JSON.parse(JSON.stringify(parentSession))); - setDeletingId(set.id); // Use set ID for deletion + setDeletingSetInfo({ sessionId: parentSession.id, setId: set.id }); } }} className="p-2 text-on-surface-variant hover:text-error hover:bg-surface-container-high rounded-full transition-colors" @@ -258,14 +270,21 @@ const History: React.FC = ({ sessions, onUpdateSession, onDeleteSe {/* DELETE CONFIRMATION DIALOG (MD3) */} - {deletingId && ( + {(deletingId || deletingSetInfo) && (
-

{t('delete_workout', lang)}

-

{t('delete_confirm', lang)}

+

+ {deletingId ? t('delete_workout', lang) : t('delete_set', lang) || 'Delete Set'} +

+

+ {deletingId ? t('delete_confirm', lang) : t('delete_set_confirm', lang) || 'Are you sure you want to delete this set?'} +

@@ -305,6 +292,8 @@ const Plans: React.FC = ({ userId, onStartPlan, lang }) => { onChange={(e: any) => setNewExName(e.target.value)} type="text" autoFocus + autocapitalize="words" + onBlur={() => setNewExName(toTitleCase(newExName))} />
diff --git a/components/Profile.tsx b/components/Profile.tsx index 0309c3a..5a7245b 100644 --- a/components/Profile.tsx +++ b/components/Profile.tsx @@ -6,6 +6,7 @@ import { getExercises, saveExercise } from '../services/storage'; import { getWeightHistory, logWeight } from '../services/weight'; import { User as UserIcon, LogOut, Save, Shield, UserPlus, Lock, Calendar, Ruler, Scale, PersonStanding, Globe, ChevronDown, ChevronUp, Trash2, Ban, KeyRound, Dumbbell, Archive, ArchiveRestore, Pencil, Plus } from 'lucide-react'; import ExerciseModal from './ExerciseModal'; +import FilledInput from './FilledInput'; import { t } from '../services/i18n'; import Snackbar from './Snackbar'; @@ -375,16 +376,14 @@ const Profile: React.FC = ({ user, onLogout, lang, onLanguageChang {t('create_exercise', lang)} -
- - setExerciseNameFilter(e.target.value)} - placeholder={t('type_to_filter', lang) || 'Type to filter...'} - className="w-full bg-transparent text-on-surface focus:outline-none" - /> -
+ setExerciseNameFilter(e.target.value)} + icon={} // No icon needed or maybe use Search icon? Profile doesn't import Search. I'll omit icon if optional. + type="text" + autoFocus={false} + />
@@ -476,19 +475,17 @@ const Profile: React.FC = ({ user, onLogout, lang, onLanguageChang {/* Create User */}

{t('create_user', lang)}

- setNewUserEmail(e.target.value)} - className="w-full bg-surface-container-high border-b border-outline-variant px-3 py-2 text-on-surface focus:outline-none rounded-t-lg" + type="email" /> - setNewUserPass(e.target.value)} - className="w-full bg-surface-container-high border-b border-outline-variant px-3 py-2 text-on-surface focus:outline-none rounded-t-lg" + type="text" /> + } /> - {showSuggestions && (
{filteredExercises.length > 0 ? ( @@ -163,8 +165,8 @@ const SetLogger: React.FC = ({ tracker, lang, onLogSet, isSporad