diff --git a/apps/mobile/app.json b/apps/mobile/app.json index 89b0386..50311fd 100644 --- a/apps/mobile/app.json +++ b/apps/mobile/app.json @@ -24,7 +24,12 @@ "foregroundImage": "./assets/adaptive-icon.png", "backgroundColor": "#ffffff" }, - "permissions": ["CAMERA", "POST_NOTIFICATIONS"] + "permissions": [ + "CAMERA", + "POST_NOTIFICATIONS", + "android.permission.CAMERA" + ], + "package": "com.anonymous.fitai" }, "web": { "favicon": "./assets/favicon.png" diff --git a/apps/mobile/jest.config.js b/apps/mobile/jest.config.js index c53f1fe..f92d0b7 100644 --- a/apps/mobile/jest.config.js +++ b/apps/mobile/jest.config.js @@ -1,11 +1,11 @@ module.exports = { - preset: 'react-native', - moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'], - setupFilesAfterEnv: ['/jest.setup.js'], - moduleNameMapping: { - '^@/(.*)$': '/src/$1', + preset: "react-native", + moduleFileExtensions: ["ts", "tsx", "js", "jsx", "json", "node"], + setupFilesAfterEnv: ["/jest.setup.js"], + moduleNameMapper: { + "^@/(.*)$": "/src/$1", }, transformIgnorePatterns: [ - 'node_modules/(?!(jest-)?react-native|@react-native|expo|@expo|@react-navigation)', + "node_modules/(?!(jest-)?react-native|@react-native|expo|@expo|@react-navigation)", ], -} \ No newline at end of file +}; diff --git a/apps/mobile/src/app/(tabs)/_layout.tsx b/apps/mobile/src/app/(tabs)/_layout.tsx index 717a189..3199a1a 100644 --- a/apps/mobile/src/app/(tabs)/_layout.tsx +++ b/apps/mobile/src/app/(tabs)/_layout.tsx @@ -33,6 +33,7 @@ export default function TabLayout() { const token = await getToken(); if (!token) { log.warn("No token available for onboarding check"); + setOnboardingChecked(true); return; } diff --git a/apps/mobile/src/app/fitness-profile.tsx b/apps/mobile/src/app/fitness-profile.tsx index 788a86d..4a25d62 100644 --- a/apps/mobile/src/app/fitness-profile.tsx +++ b/apps/mobile/src/app/fitness-profile.tsx @@ -16,8 +16,7 @@ import { Ionicons } from "@expo/vector-icons"; import { useTheme } from "../contexts/ThemeContext"; import { MinimalCard } from "../components/MinimalCard"; import { MinimalButton } from "../components/MinimalButton"; -import { IconContainer } from "../components/IconContainer"; -import { API_BASE_URL } from "../config/api"; +import { fitnessProfileApi, type FitnessProfile } from "../api/fitnessProfile"; interface FitnessProfileData { height?: number; @@ -110,39 +109,31 @@ export default function FitnessProfileScreen() { try { setFetchingProfile(true); const token = await getToken(); - const response = await fetch( - `${API_BASE_URL}/api/profile/fitness?userId=${userId}`, - { - headers: { - Authorization: `Bearer ${token}`, - }, - }, - ); + if (!token || !userId) { + return; + } - if (response.ok) { - const data = await response.json(); - if (data.profile) { - let activityLevel = data.profile.activityLevel || ""; - if (activityLevel === "light") activityLevel = "lightly_active"; - if (activityLevel === "moderate") activityLevel = "moderately_active"; - if (activityLevel === "active") activityLevel = "very_active"; + const profile = await fitnessProfileApi.getFitnessProfile(token); - setProfileData({ - height: data.profile.height, - weight: data.profile.weight, - age: data.profile.age, - gender: data.profile.gender || "", - fitnessGoal: Array.isArray(data.profile.fitnessGoals) - ? data.profile.fitnessGoals[0] - : typeof data.profile.fitnessGoals === "string" - ? JSON.parse(data.profile.fitnessGoals)[0] - : "", - activityLevel: activityLevel, - medicalConditions: data.profile.medicalConditions || "", - allergies: data.profile.allergies || "", - injuries: data.profile.injuries || "", - }); - } + if (profile) { + let activityLevel = profile.activityLevel || ""; + if (activityLevel === "light") activityLevel = "lightly_active"; + if (activityLevel === "moderate") activityLevel = "moderately_active"; + if (activityLevel === "active") activityLevel = "very_active"; + + setProfileData({ + height: profile.height, + weight: profile.weight, + age: profile.age, + gender: profile.gender || "", + fitnessGoal: Array.isArray(profile.fitnessGoals) + ? profile.fitnessGoals[0] + : "", + activityLevel: activityLevel, + medicalConditions: profile.medicalConditions || "", + allergies: profile.allergies || "", + injuries: profile.injuries || "", + }); } } catch (error) { console.error("Error fetching profile:", error); @@ -155,37 +146,31 @@ export default function FitnessProfileScreen() { try { setLoading(true); const token = await getToken(); + if (!token || !userId) { + Alert.alert("Error", "Authentication required"); + return; + } - const dataToSave = { + const dataToSave: Omit = { userId: userId, height: profileData.height, weight: profileData.weight, age: profileData.age, - gender: profileData.gender || undefined, + gender: (profileData.gender as FitnessProfile["gender"]) || undefined, fitnessGoals: profileData.fitnessGoal ? [profileData.fitnessGoal] : [], - activityLevel: profileData.activityLevel || undefined, + activityLevel: + (profileData.activityLevel as FitnessProfile["activityLevel"]) || + undefined, medicalConditions: profileData.medicalConditions, allergies: profileData.allergies, injuries: profileData.injuries, }; - const response = await fetch(`${API_BASE_URL}/api/profile/fitness`, { - method: "POST", - headers: { - "Content-Type": "application/json", - Authorization: `Bearer ${token}`, - }, - body: JSON.stringify(dataToSave), - }); + await fitnessProfileApi.createFitnessProfile(dataToSave, token); - if (response.ok) { - Alert.alert("Success", "Fitness profile saved successfully!", [ - { text: "OK", onPress: () => router.back() }, - ]); - } else { - const error = await response.json(); - Alert.alert("Error", error.error || "Failed to save profile"); - } + Alert.alert("Success", "Fitness profile saved successfully!", [ + { text: "OK", onPress: () => router.back() }, + ]); } catch (error) { console.error("Error saving profile:", error); Alert.alert("Error", "Failed to save fitness profile");