Exercise management enhanced
This commit is contained in:
@@ -3,7 +3,8 @@ import React, { useState, useEffect } from 'react';
|
||||
import { User, Language, ExerciseDef, ExerciseType } from '../types';
|
||||
import { createUser, changePassword, updateUserProfile, getCurrentUserProfile, getUsers, deleteUser, toggleBlockUser, adminResetPassword, getMe } from '../services/auth';
|
||||
import { getExercises, saveExercise } from '../services/storage';
|
||||
import { User as UserIcon, LogOut, Save, Shield, UserPlus, Lock, Calendar, Ruler, Scale, PersonStanding, Globe, ChevronDown, ChevronUp, Trash2, Ban, KeyRound, Dumbbell, Archive, ArchiveRestore, Pencil, X, Plus, Percent } from 'lucide-react';
|
||||
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 { t } from '../services/i18n';
|
||||
import Snackbar from './Snackbar';
|
||||
|
||||
@@ -45,10 +46,16 @@ const Profile: React.FC<ProfileProps> = ({ user, onLogout, lang, onLanguageChang
|
||||
const [showArchived, setShowArchived] = useState(false);
|
||||
const [editingExercise, setEditingExercise] = useState<ExerciseDef | null>(null);
|
||||
const [isCreatingEx, setIsCreatingEx] = useState(false);
|
||||
// New exercise form
|
||||
const [newExName, setNewExName] = useState('');
|
||||
const [newExType, setNewExType] = useState<ExerciseType>(ExerciseType.STRENGTH);
|
||||
const [newExBw, setNewExBw] = useState('100');
|
||||
|
||||
const exerciseTypeLabels: Record<ExerciseType, string> = {
|
||||
[ExerciseType.STRENGTH]: t('type_strength', lang),
|
||||
[ExerciseType.BODYWEIGHT]: t('type_bodyweight', lang),
|
||||
[ExerciseType.CARDIO]: t('type_cardio', lang),
|
||||
[ExerciseType.STATIC]: t('type_static', lang),
|
||||
[ExerciseType.HIGH_JUMP]: t('type_height', lang),
|
||||
[ExerciseType.LONG_JUMP]: t('type_dist', lang),
|
||||
[ExerciseType.PLYOMETRIC]: t('type_jump', lang),
|
||||
};
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
@@ -178,19 +185,10 @@ const Profile: React.FC<ProfileProps> = ({ user, onLogout, lang, onLanguageChang
|
||||
}
|
||||
};
|
||||
|
||||
const handleCreateExercise = () => {
|
||||
if (newExName.trim()) {
|
||||
const newEx: ExerciseDef = {
|
||||
id: crypto.randomUUID(),
|
||||
name: newExName.trim(),
|
||||
type: newExType,
|
||||
bodyWeightPercentage: newExType === ExerciseType.BODYWEIGHT ? parseFloat(newExBw) : undefined
|
||||
};
|
||||
saveExercise(user.id, newEx);
|
||||
setNewExName('');
|
||||
setIsCreatingEx(false);
|
||||
refreshExercises();
|
||||
}
|
||||
const handleCreateExercise = async (newEx: ExerciseDef) => {
|
||||
await saveExercise(user.id, newEx);
|
||||
setIsCreatingEx(false);
|
||||
await refreshExercises();
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -302,7 +300,7 @@ const Profile: React.FC<ProfileProps> = ({ user, onLogout, lang, onLanguageChang
|
||||
<div key={ex.id} className={`p-3 rounded-lg flex justify-between items-center border border-outline-variant/20 ${ex.isArchived ? 'bg-surface-container-low opacity-60' : 'bg-surface-container-high'}`}>
|
||||
<div className="overflow-hidden mr-2">
|
||||
<div className="font-medium text-sm text-on-surface truncate">{ex.name}</div>
|
||||
<div className="text-xs text-on-surface-variant">{ex.type}</div>
|
||||
<div className="text-xs text-on-surface-variant">{exerciseTypeLabels[ex.type]}</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-1 shrink-0">
|
||||
<button onClick={() => setEditingExercise(ex)} className="p-2 text-on-surface-variant hover:text-primary hover:bg-white/5 rounded-full">
|
||||
@@ -498,62 +496,12 @@ const Profile: React.FC<ProfileProps> = ({ user, onLogout, lang, onLanguageChang
|
||||
|
||||
{/* Create Exercise Modal */}
|
||||
{isCreatingEx && (
|
||||
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-sm p-4">
|
||||
<div className="bg-surface-container w-full max-w-sm rounded-[28px] p-6 shadow-elevation-3">
|
||||
<h3 className="text-xl font-normal text-on-surface mb-4">{t('create_exercise', lang)}</h3>
|
||||
<div className="space-y-4">
|
||||
<div className="bg-surface-container-high rounded-t-lg border-b border-outline-variant px-3 py-2">
|
||||
<label className="text-[10px] text-on-surface-variant font-medium">{t('ex_name', lang)}</label>
|
||||
<input
|
||||
value={newExName}
|
||||
onChange={(e) => setNewExName(e.target.value)}
|
||||
className="w-full bg-transparent text-on-surface focus:outline-none"
|
||||
autoFocus
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="text-[10px] text-on-surface-variant font-medium mb-2 block">{t('ex_type', lang)}</label>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
<button
|
||||
onClick={() => setNewExType(ExerciseType.STRENGTH)}
|
||||
className={`px-3 py-1 rounded-full text-xs border ${newExType === ExerciseType.STRENGTH ? 'bg-primary text-on-primary border-transparent' : 'border-outline text-on-surface-variant'}`}
|
||||
>
|
||||
{t('type_strength', lang)}
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setNewExType(ExerciseType.BODYWEIGHT)}
|
||||
className={`px-3 py-1 rounded-full text-xs border ${newExType === ExerciseType.BODYWEIGHT ? 'bg-primary text-on-primary border-transparent' : 'border-outline text-on-surface-variant'}`}
|
||||
>
|
||||
{t('type_bodyweight', lang)}
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setNewExType(ExerciseType.CARDIO)}
|
||||
className={`px-3 py-1 rounded-full text-xs border ${newExType === ExerciseType.CARDIO ? 'bg-primary text-on-primary border-transparent' : 'border-outline text-on-surface-variant'}`}
|
||||
>
|
||||
{t('type_cardio', lang)}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{newExType === ExerciseType.BODYWEIGHT && (
|
||||
<div className="bg-surface-container-high rounded-t-lg border-b border-outline-variant px-3 py-2">
|
||||
<label className="text-[10px] text-on-surface-variant font-medium">{t('body_weight_percent', lang)}</label>
|
||||
<input
|
||||
type="number"
|
||||
value={newExBw}
|
||||
onChange={(e) => setNewExBw(e.target.value)}
|
||||
className="w-full bg-transparent text-on-surface focus:outline-none"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<div className="flex justify-end gap-2 pt-2">
|
||||
<button onClick={() => setIsCreatingEx(false)} className="px-4 py-2 rounded-full text-primary font-medium hover:bg-white/5">{t('cancel', lang)}</button>
|
||||
<button onClick={handleCreateExercise} className="px-4 py-2 rounded-full bg-primary text-on-primary font-medium">{t('create_btn', lang)}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<ExerciseModal
|
||||
isOpen={isCreatingEx}
|
||||
onClose={() => setIsCreatingEx(false)}
|
||||
onSave={handleCreateExercise}
|
||||
lang={lang}
|
||||
/>
|
||||
)}
|
||||
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user