-
Welcome, {user.username}
-
- Role: {user.role}
-
+
+
+
Welcome, {user.username}
+
+ Role: {user.role}
+
+
+
+ Edit Profile
+
+
diff --git a/frontend/src/pages/Profile.tsx b/frontend/src/pages/Profile.tsx
new file mode 100644
index 0000000..5fb7833
--- /dev/null
+++ b/frontend/src/pages/Profile.tsx
@@ -0,0 +1,252 @@
+import { useEffect, useState } from 'react'
+import { useNavigate, Link } from 'react-router-dom'
+
+interface Diet {
+ id: number
+ name: string
+ description: string
+}
+
+interface UserProfile {
+ id: number
+ username: string
+ role: string
+ height_cm: number | null
+ blood_type: string | null
+ birthdate: string | null
+ smoking: boolean | null
+ alcohol: boolean | null
+ diet: string | null
+}
+
+export function ProfilePage() {
+ const navigate = useNavigate()
+ const [loading, setLoading] = useState(true)
+ const [saving, setSaving] = useState(false)
+ const [message, setMessage] = useState<{ type: 'success' | 'error'; text: string } | null>(null)
+ const [diets, setDiets] = useState([])
+
+ // Form state
+ const [userId, setUserId] = useState(null)
+ const [username, setUsername] = useState('')
+ const [heightCm, setHeightCm] = useState('')
+ const [bloodType, setBloodType] = useState('')
+ const [birthdate, setBirthdate] = useState('')
+ const [smoking, setSmoking] = useState(null)
+ const [alcohol, setAlcohol] = useState(null)
+ const [dietId, setDietId] = useState(null)
+
+ useEffect(() => {
+ // Fetch current user and diets
+ Promise.all([
+ fetch('/api/auth/me', { credentials: 'include' }).then(r => r.json()),
+ fetch('/api/diets', { credentials: 'include' }).then(r => r.json()),
+ ])
+ .then(([authData, dietsData]) => {
+ if (!authData.user) {
+ navigate('/login')
+ return
+ }
+ setDiets(dietsData)
+
+ // Now fetch full user profile
+ return fetch(`/api/users/${authData.user.id}`, { credentials: 'include' })
+ .then(r => r.json())
+ .then((profile: UserProfile) => {
+ setUserId(profile.id)
+ setUsername(profile.username)
+ setHeightCm(profile.height_cm?.toString() || '')
+ setBloodType(profile.blood_type || '')
+ setBirthdate(profile.birthdate || '')
+ setSmoking(profile.smoking)
+ setAlcohol(profile.alcohol)
+ // Find diet ID from name
+ const diet = dietsData.find((d: Diet) => d.name === profile.diet)
+ setDietId(diet?.id || null)
+ })
+ })
+ .catch(() => navigate('/login'))
+ .finally(() => setLoading(false))
+ }, [navigate])
+
+ const handleSubmit = async (e: React.FormEvent) => {
+ e.preventDefault()
+ if (!userId) return
+
+ setSaving(true)
+ setMessage(null)
+
+ try {
+ const res = await fetch(`/api/users/${userId}`, {
+ method: 'PUT',
+ headers: { 'Content-Type': 'application/json' },
+ credentials: 'include',
+ body: JSON.stringify({
+ height_cm: heightCm ? parseFloat(heightCm) : null,
+ blood_type: bloodType || null,
+ birthdate: birthdate || null,
+ smoking,
+ alcohol,
+ diet_id: dietId,
+ }),
+ })
+
+ if (res.ok) {
+ setMessage({ type: 'success', text: 'Profile updated successfully' })
+ } else {
+ setMessage({ type: 'error', text: 'Failed to update profile' })
+ }
+ } catch {
+ setMessage({ type: 'error', text: 'Network error' })
+ } finally {
+ setSaving(false)
+ }
+ }
+
+ if (loading) {
+ return Loading...
+ }
+
+ return (
+
+
+ ← Back to Dashboard
+ Profile
+
+
+
+
+ )
+}