diff --git a/components/ExerciseModal.tsx b/components/ExerciseModal.tsx index 6fd7a62..c2e4bc0 100644 --- a/components/ExerciseModal.tsx +++ b/components/ExerciseModal.tsx @@ -9,12 +9,14 @@ interface ExerciseModalProps { onClose: () => void; onSave: (exercise: ExerciseDef) => Promise | void; lang: Language; + existingExercises?: ExerciseDef[]; } -const ExerciseModal: React.FC = ({ isOpen, onClose, onSave, lang }) => { +const ExerciseModal: React.FC = ({ isOpen, onClose, onSave, lang, existingExercises = [] }) => { const [newName, setNewName] = useState(''); const [newType, setNewType] = useState(ExerciseType.STRENGTH); const [newBwPercentage, setNewBwPercentage] = useState('100'); + const [error, setError] = useState(''); const exerciseTypeLabels: Record = { [ExerciseType.STRENGTH]: t('type_strength', lang), @@ -28,9 +30,21 @@ const ExerciseModal: React.FC = ({ isOpen, onClose, onSave, const handleCreateExercise = async () => { if (!newName.trim()) return; + + // Check for duplicate name (case-insensitive) + const trimmedName = newName.trim(); + const isDuplicate = existingExercises.some( + ex => ex.name.toLowerCase() === trimmedName.toLowerCase() + ); + + if (isDuplicate) { + setError(t('exercise_name_exists', lang) || 'An exercise with this name already exists'); + return; + } + const newEx: ExerciseDef = { id: crypto.randomUUID(), - name: newName.trim(), + name: trimmedName, type: newType, ...(newType === ExerciseType.BODYWEIGHT && { bodyWeightPercentage: parseFloat(newBwPercentage) || 100 }) }; @@ -38,6 +52,7 @@ const ExerciseModal: React.FC = ({ isOpen, onClose, onSave, setNewName(''); setNewType(ExerciseType.STRENGTH); setNewBwPercentage('100'); + setError(''); onClose(); }; @@ -52,13 +67,21 @@ const ExerciseModal: React.FC = ({ isOpen, onClose, onSave,
- setNewName(e.target.value)} - type="text" - autoFocus - /> +
+ { + setNewName(e.target.value); + setError(''); // Clear error when user types + }} + type="text" + autoFocus + /> + {error && ( +

{error}

+ )} +
diff --git a/components/Profile.tsx b/components/Profile.tsx index d57ee72..36e4c33 100644 --- a/components/Profile.tsx +++ b/components/Profile.tsx @@ -514,6 +514,7 @@ const Profile: React.FC = ({ user, onLogout, lang, onLanguageChang onClose={() => setIsCreatingEx(false)} onSave={handleCreateExercise} lang={lang} + existingExercises={exercises} /> )} diff --git a/server/prisma/dev.db b/server/prisma/dev.db index 983d7ac..555bd62 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 013f349..cbeeece 100644 --- a/services/i18n.ts +++ b/services/i18n.ts @@ -155,6 +155,7 @@ const translations = { show_archived: 'Show Archived', filter_by_name: 'Filter by name', type_to_filter: 'Type to filter...', + exercise_name_exists: 'An exercise with this name already exists', profile_saved: 'Profile saved successfully', }, ru: { @@ -304,6 +305,7 @@ const translations = { show_archived: 'Показать архивные', filter_by_name: 'Фильтр по названию', type_to_filter: 'Введите для фильтрации...', + exercise_name_exists: 'Упражнение с таким названием уже существует', profile_saved: 'Профиль успешно сохранен', } };