New exercise name autofill. Log Set button animation.

This commit is contained in:
AG
2025-12-12 21:48:46 +02:00
parent c7f5c4048c
commit f169c7c4d3
8 changed files with 67 additions and 19 deletions

View File

@@ -15,15 +15,23 @@ interface ExerciseModalProps {
onSave: (exercise: ExerciseDef) => Promise<void> | void;
lang: Language;
existingExercises?: ExerciseDef[];
initialName?: string;
}
const ExerciseModal: React.FC<ExerciseModalProps> = ({ isOpen, onClose, onSave, lang, existingExercises = [] }) => {
const [newName, setNewName] = useState('');
const ExerciseModal: React.FC<ExerciseModalProps> = ({ isOpen, onClose, onSave, lang, existingExercises = [], initialName = '' }) => {
const [newName, setNewName] = useState(initialName);
const [newType, setNewType] = useState<ExerciseType>(ExerciseType.STRENGTH);
const [newBwPercentage, setNewBwPercentage] = useState<string>('100');
const [isUnilateral, setIsUnilateral] = useState(false);
const [error, setError] = useState<string>('');
// Update newName when modal opens or initialName changes
React.useEffect(() => {
if (isOpen) {
setNewName(initialName);
}
}, [isOpen, initialName]);
const exerciseTypeLabels: Record<ExerciseType, string> = {
[ExerciseType.STRENGTH]: t('type_strength', lang),
[ExerciseType.BODYWEIGHT]: t('type_bodyweight', lang),
@@ -69,12 +77,12 @@ const ExerciseModal: React.FC<ExerciseModalProps> = ({ isOpen, onClose, onSave,
isOpen={isOpen}
onClose={() => {
console.log('ExerciseModal onClose');
setNewName(''); // Reset on close if desired
onClose();
}}
title={t('create_exercise', lang)}
width="md"
>
{console.log('ExerciseModal Rendering. isOpen:', isOpen)}
<div className="space-y-6">
<div>
<FilledInput

View File

@@ -75,18 +75,21 @@ const FilledInput: React.FC<FilledInputProps> = ({
<button
type="button"
onClick={handleClear}
className={`absolute top-1/2 -translate-y-1/2 p-2 text-on-surface-variant hover:text-on-surface rounded-full opacity-0 group-hover:opacity-100 focus:opacity-100 transition-opacity ${rightElement ? 'right-12' : 'right-2'}`}
aria-label="Clear input"
className={`absolute top-1/2 -translate-y-1/2 p-2 text-on-surface-variant hover:text-on-surface rounded-full transition-opacity ${rightElement ? 'right-12' : 'right-2'}`}
tabIndex={-1}
>
<X size={16} />
</button>
)}
{rightElement && (
<div className="absolute right-2 top-1/2 -translate-y-1/2 z-10">
{rightElement}
</div>
)}
</div>
{
rightElement && (
<div className="absolute right-2 top-1/2 -translate-y-1/2 z-10">
{rightElement}
</div>
)
}
</div >
);
};

View File

@@ -50,7 +50,7 @@ interface SortablePlanStepProps {
lang: Language;
}
const SortablePlanStep = ({ step, index, toggleWeighted, updateRestTime, removeStep, lang }: SortablePlanStepProps) => {
const SortablePlanStep: React.FC<SortablePlanStepProps> = ({ step, index, toggleWeighted, updateRestTime, removeStep, lang }) => {
const {
attributes,
listeners,

View File

@@ -299,6 +299,7 @@ const ActiveSessionView: React.FC<ActiveSessionViewProps> = ({ tracker, activeSe
onSave={handleCreateExercise}
lang={lang}
existingExercises={exercises}
initialName={tracker.searchQuery}
/>
)}

View File

@@ -8,7 +8,7 @@ import { useTracker } from './useTracker';
interface SetLoggerProps {
tracker: ReturnType<typeof useTracker>;
lang: Language;
onLogSet: () => void;
onLogSet: () => Promise<void> | void;
isSporadic?: boolean;
}
@@ -39,7 +39,31 @@ const SetLogger: React.FC<SetLoggerProps> = ({ tracker, lang, onLogSet, isSporad
setUnilateralSide
} = tracker;
const [localSuccess, setLocalSuccess] = React.useState(false);
React.useEffect(() => {
if (localSuccess) {
const timer = setTimeout(() => {
setLocalSuccess(false);
}, 750);
return () => clearTimeout(timer);
}
}, [localSuccess]);
const handleLogClick = async () => {
try {
const result = onLogSet();
if (result instanceof Promise) {
await result;
}
setLocalSuccess(true);
} catch (error) {
console.error("Failed to log set:", error);
}
};
const isPlanFinished = activePlan && currentStepIndex >= activePlan.steps.length;
const showSuccess = (isSporadic && sporadicSuccess) || localSuccess;
return (
<div className="space-y-6">
@@ -174,14 +198,14 @@ const SetLogger: React.FC<SetLoggerProps> = ({ tracker, lang, onLogSet, isSporad
</div>
<button
onClick={onLogSet}
className={`w-full h-14 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 ${isSporadic && sporadicSuccess
onClick={handleLogClick}
className={`w-full h-14 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 ${showSuccess
? 'bg-green-500 text-white'
: 'bg-primary-container text-on-primary-container'
}`}
>
{isSporadic && sporadicSuccess ? <CheckCircle size={24} /> : <Plus size={24} />}
<span>{isSporadic && sporadicSuccess ? t('saved', lang) : t('log_set', lang)}</span>
{showSuccess ? <CheckCircle size={24} className="animate-in zoom-in spin-in-90 duration-300" /> : <Plus size={24} />}
<span>{showSuccess ? t('saved', lang) : t('log_set', lang)}</span>
</button>
</div>
)}

View File

@@ -153,6 +153,7 @@ const SporadicView: React.FC<SporadicViewProps> = ({ tracker, lang }) => {
onSave={handleCreateExercise}
lang={lang}
existingExercises={exercises}
initialName={tracker.searchQuery}
/>
)}