From 84417847fd6fcec14648ace5053f58f182be9583 Mon Sep 17 00:00:00 2001 From: AG Date: Thu, 20 Nov 2025 22:56:38 +0200 Subject: [PATCH] Profile saving works --- App.tsx | 5 +++++ components/Profile.tsx | 27 +++++++++++++++++---------- server/prisma/dev.db | Bin 61440 -> 61440 bytes server/src/routes/auth.ts | 7 +++++++ services/api.ts | 9 +++++++++ types.ts | 2 +- 6 files changed, 39 insertions(+), 11 deletions(-) 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 6111a95484bcac480756b90e6a4489d8f669933b..bfbc96ee90a929c3e88972d34061f58cc0015701 100644 GIT binary patch delta 68 zcmV-K0K5Ny-~)i*1CSd5ACVkG0UxnorY{7a01uzD5g?Qg3t(RX2vt=91r-7jliW`v algvLP4O9S<)Ugx*O+idW0KK#RKLW7T?-jWK delta 68 zcmZp8z})bFd4e>f=tLQ3M$wH4%k)|1G4RjZET}MrpDTfpBQ%tqL6k{w@*RJb$!G0V W_#8qL7(l?+(Z`ix%jSRfOd9~Q=M%F4 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; }