diff --git a/App.tsx b/App.tsx index e6cb5a9..aed79ae 100644 --- a/App.tsx +++ b/App.tsx @@ -69,6 +69,10 @@ function App() { setLanguage(lang); }; + const handleUserUpdate = (updatedUser: User) => { + setCurrentUser(updatedUser); + }; + const handleStartSession = (plan?: WorkoutPlan) => { if (!currentUser) return; @@ -179,6 +183,7 @@ function App() { onLogout={handleLogout} lang={language} onLanguageChange={handleLanguageChange} + onUserUpdate={handleUserUpdate} /> )} diff --git a/components/Profile.tsx b/components/Profile.tsx index da55e11..069d56d 100644 --- a/components/Profile.tsx +++ b/components/Profile.tsx @@ -1,7 +1,7 @@ import React, { useState, useEffect } from 'react'; import { User, Language, ExerciseDef, ExerciseType } from '../types'; -import { createUser, changePassword, updateUserProfile, getCurrentUserProfile, getUsers, deleteUser, toggleBlockUser, adminResetPassword } from '../services/auth'; +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 { t } from '../services/i18n'; @@ -12,9 +12,10 @@ interface ProfileProps { onLogout: () => void; lang: Language; onLanguageChange: (lang: Language) => void; + onUserUpdate?: (user: User) => void; } -const Profile: React.FC = ({ user, onLogout, lang, onLanguageChange }) => { +const Profile: React.FC = ({ user, onLogout, lang, onLanguageChange, onUserUpdate }) => { // Profile Data const [weight, setWeight] = useState(''); const [height, setHeight] = useState(''); @@ -51,19 +52,20 @@ const Profile: React.FC = ({ user, onLogout, lang, onLanguageChang useEffect(() => { - const p = getCurrentUserProfile(user.id); - if (p) { - if (p.weight) setWeight(p.weight.toString()); - if (p.height) setHeight(p.height.toString()); - if (p.gender) setGender(p.gender); - if (p.birthDate) setBirthDate(new Date(p.birthDate).toISOString().split('T')[0]); + // Load profile data from user object (comes from /auth/me endpoint) + if (user.profile) { + if (user.profile.weight) setWeight(user.profile.weight.toString()); + if (user.profile.height) setHeight(user.profile.height.toString()); + if (user.profile.gender) setGender(user.profile.gender); + if (user.profile.birthDate) setBirthDate(new Date(user.profile.birthDate).toISOString().split('T')[0]); } if (user.role === 'ADMIN') { refreshUserList(); } refreshExercises(); - }, [user.id, user.role]); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [user.id, user.role, JSON.stringify(user.profile)]); const refreshUserList = () => { setAllUsers(getUsers()); @@ -89,12 +91,17 @@ const Profile: React.FC = ({ user, onLogout, lang, onLanguageChang weight: parseFloat(weight) || undefined, height: parseFloat(height) || undefined, gender: gender as any, - birthDate: birthDate ? new Date(birthDate).getTime() : undefined, + birthDate: birthDate ? new Date(birthDate).toISOString() : undefined, language: lang }); if (res.success) { showSnackbar(t('profile_saved', lang) || 'Profile saved successfully', 'success'); + // Refetch user data to update the profile in the app state + const userRes = await getMe(); + if (userRes.success && userRes.user && onUserUpdate) { + onUserUpdate(userRes.user); + } } else { showSnackbar(res.error || 'Failed to save profile', 'error'); } diff --git a/server/prisma/dev.db b/server/prisma/dev.db index 6111a95..bfbc96e 100644 Binary files a/server/prisma/dev.db and b/server/prisma/dev.db differ diff --git a/server/src/routes/auth.ts b/server/src/routes/auth.ts index 9c83f72..134a183 100644 --- a/server/src/routes/auth.ts +++ b/server/src/routes/auth.ts @@ -105,6 +105,13 @@ router.patch('/profile', async (req, res) => { if (!token) return res.status(401).json({ error: 'Unauthorized' }); const { userId, profile } = req.body; + console.log('DEBUG: Updating profile for', userId, profile); + + // Convert birthDate from timestamp to Date object if needed + if (profile.birthDate) { + // Handle both number (timestamp) and string (ISO) + profile.birthDate = new Date(profile.birthDate); + } // Verify token const decoded = jwt.verify(token, JWT_SECRET) as any; diff --git a/services/api.ts b/services/api.ts index e173263..75ad25d 100644 --- a/services/api.ts +++ b/services/api.ts @@ -34,5 +34,14 @@ export const api = { }); if (!res.ok) throw new Error(await res.text()); return res.json(); + }, + patch: async (endpoint: string, data: any) => { + const res = await fetch(`${API_URL}${endpoint}`, { + method: 'PATCH', + headers: headers(), + body: JSON.stringify(data) + }); + if (!res.ok) throw new Error(await res.text()); + return res.json(); } }; diff --git a/types.ts b/types.ts index 235029b..e3c7866 100644 --- a/types.ts +++ b/types.ts @@ -68,7 +68,7 @@ export interface UserProfile { weight?: number; height?: number; gender?: 'MALE' | 'FEMALE' | 'OTHER'; - birthDate?: number; // timestamp + birthDate?: number | string; // timestamp or ISO string language?: Language; }