Profile saving works
This commit is contained in:
5
App.tsx
5
App.tsx
@@ -69,6 +69,10 @@ function App() {
|
|||||||
setLanguage(lang);
|
setLanguage(lang);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleUserUpdate = (updatedUser: User) => {
|
||||||
|
setCurrentUser(updatedUser);
|
||||||
|
};
|
||||||
|
|
||||||
const handleStartSession = (plan?: WorkoutPlan) => {
|
const handleStartSession = (plan?: WorkoutPlan) => {
|
||||||
if (!currentUser) return;
|
if (!currentUser) return;
|
||||||
|
|
||||||
@@ -179,6 +183,7 @@ function App() {
|
|||||||
onLogout={handleLogout}
|
onLogout={handleLogout}
|
||||||
lang={language}
|
lang={language}
|
||||||
onLanguageChange={handleLanguageChange}
|
onLanguageChange={handleLanguageChange}
|
||||||
|
onUserUpdate={handleUserUpdate}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
|
|
||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import { User, Language, ExerciseDef, ExerciseType } from '../types';
|
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 { 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, X, Plus, Percent } from 'lucide-react';
|
||||||
import { t } from '../services/i18n';
|
import { t } from '../services/i18n';
|
||||||
@@ -12,9 +12,10 @@ interface ProfileProps {
|
|||||||
onLogout: () => void;
|
onLogout: () => void;
|
||||||
lang: Language;
|
lang: Language;
|
||||||
onLanguageChange: (lang: Language) => void;
|
onLanguageChange: (lang: Language) => void;
|
||||||
|
onUserUpdate?: (user: User) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Profile: React.FC<ProfileProps> = ({ user, onLogout, lang, onLanguageChange }) => {
|
const Profile: React.FC<ProfileProps> = ({ user, onLogout, lang, onLanguageChange, onUserUpdate }) => {
|
||||||
// Profile Data
|
// Profile Data
|
||||||
const [weight, setWeight] = useState<string>('');
|
const [weight, setWeight] = useState<string>('');
|
||||||
const [height, setHeight] = useState<string>('');
|
const [height, setHeight] = useState<string>('');
|
||||||
@@ -51,19 +52,20 @@ const Profile: React.FC<ProfileProps> = ({ user, onLogout, lang, onLanguageChang
|
|||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const p = getCurrentUserProfile(user.id);
|
// Load profile data from user object (comes from /auth/me endpoint)
|
||||||
if (p) {
|
if (user.profile) {
|
||||||
if (p.weight) setWeight(p.weight.toString());
|
if (user.profile.weight) setWeight(user.profile.weight.toString());
|
||||||
if (p.height) setHeight(p.height.toString());
|
if (user.profile.height) setHeight(user.profile.height.toString());
|
||||||
if (p.gender) setGender(p.gender);
|
if (user.profile.gender) setGender(user.profile.gender);
|
||||||
if (p.birthDate) setBirthDate(new Date(p.birthDate).toISOString().split('T')[0]);
|
if (user.profile.birthDate) setBirthDate(new Date(user.profile.birthDate).toISOString().split('T')[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (user.role === 'ADMIN') {
|
if (user.role === 'ADMIN') {
|
||||||
refreshUserList();
|
refreshUserList();
|
||||||
}
|
}
|
||||||
refreshExercises();
|
refreshExercises();
|
||||||
}, [user.id, user.role]);
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [user.id, user.role, JSON.stringify(user.profile)]);
|
||||||
|
|
||||||
const refreshUserList = () => {
|
const refreshUserList = () => {
|
||||||
setAllUsers(getUsers());
|
setAllUsers(getUsers());
|
||||||
@@ -89,12 +91,17 @@ const Profile: React.FC<ProfileProps> = ({ user, onLogout, lang, onLanguageChang
|
|||||||
weight: parseFloat(weight) || undefined,
|
weight: parseFloat(weight) || undefined,
|
||||||
height: parseFloat(height) || undefined,
|
height: parseFloat(height) || undefined,
|
||||||
gender: gender as any,
|
gender: gender as any,
|
||||||
birthDate: birthDate ? new Date(birthDate).getTime() : undefined,
|
birthDate: birthDate ? new Date(birthDate).toISOString() : undefined,
|
||||||
language: lang
|
language: lang
|
||||||
});
|
});
|
||||||
|
|
||||||
if (res.success) {
|
if (res.success) {
|
||||||
showSnackbar(t('profile_saved', lang) || 'Profile saved successfully', '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 {
|
} else {
|
||||||
showSnackbar(res.error || 'Failed to save profile', 'error');
|
showSnackbar(res.error || 'Failed to save profile', 'error');
|
||||||
}
|
}
|
||||||
|
|||||||
Binary file not shown.
@@ -105,6 +105,13 @@ router.patch('/profile', async (req, res) => {
|
|||||||
if (!token) return res.status(401).json({ error: 'Unauthorized' });
|
if (!token) return res.status(401).json({ error: 'Unauthorized' });
|
||||||
|
|
||||||
const { userId, profile } = req.body;
|
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
|
// Verify token
|
||||||
const decoded = jwt.verify(token, JWT_SECRET) as any;
|
const decoded = jwt.verify(token, JWT_SECRET) as any;
|
||||||
|
|||||||
@@ -34,5 +34,14 @@ export const api = {
|
|||||||
});
|
});
|
||||||
if (!res.ok) throw new Error(await res.text());
|
if (!res.ok) throw new Error(await res.text());
|
||||||
return res.json();
|
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();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
2
types.ts
2
types.ts
@@ -68,7 +68,7 @@ export interface UserProfile {
|
|||||||
weight?: number;
|
weight?: number;
|
||||||
height?: number;
|
height?: number;
|
||||||
gender?: 'MALE' | 'FEMALE' | 'OTHER';
|
gender?: 'MALE' | 'FEMALE' | 'OTHER';
|
||||||
birthDate?: number; // timestamp
|
birthDate?: number | string; // timestamp or ISO string
|
||||||
language?: Language;
|
language?: Language;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user