import React, { useState, useEffect } from "react"; import { View, Text, TextInput, TouchableOpacity, StyleSheet, ScrollView, Alert, ActivityIndicator, } from "react-native"; import { useUser, useAuth } from "@clerk/clerk-expo"; import { useRouter } from "expo-router"; import { fitnessProfileApi } from "@/api/fitnessProfile"; export default function OnboardingScreen() { const { user } = useUser(); const { getToken } = useAuth(); const router = useRouter(); const [isSubmitting, setIsSubmitting] = useState(false); const [gyms, setGyms] = useState< Array<{ id: string; name: string; location?: string }> >([]); const [gymsLoading, setGymsLoading] = useState(false); const [selectedGymId, setSelectedGymId] = useState(null); useEffect(() => { const loadGyms = async () => { try { setGymsLoading(true); const token = await getToken(); const res = await fetch("/api/gyms", { headers: token ? { Authorization: `Bearer ${token}` } : undefined, }); const data = await res.json(); if (Array.isArray(data)) { setGyms(data); } } catch (e) { console.error("Failed to fetch gyms:", e); } finally { setGymsLoading(false); } }; loadGyms(); }, []); const [fitnessProfile, setFitnessProfile] = useState({ height: "", weight: "", goals: "", fitnessLevel: "beginner", medicalConditions: "", dietaryRestrictions: "", preferredWorkoutTime: "morning", workoutFrequency: "3", }); const handleSubmit = async () => { if (!user?.id) return; try { setIsSubmitting(true); // Validate required fields if (!fitnessProfile.height || !fitnessProfile.weight) { Alert.alert("Error", "Please enter your height and weight"); return; } const token = await getToken(); if (!token) { throw new Error("Authentication token not available"); } // If gym was selected or cleared, patch user's gym selection first // selectedGymId: string gym id, or null to proceed without gym try { await fetch("/api/users/gym", { method: "PATCH", headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}`, }, body: JSON.stringify({ gymId: selectedGymId }), }); } catch (e) { console.warn("Failed to update gym selection:", e); } const fitnessData = { clientId: user.id, height: parseFloat(fitnessProfile.height), weight: parseFloat(fitnessProfile.weight), goals: fitnessProfile.goals || undefined, fitnessLevel: fitnessProfile.fitnessLevel as | "beginner" | "intermediate" | "advanced", medicalConditions: fitnessProfile.medicalConditions || undefined, dietaryRestrictions: fitnessProfile.dietaryRestrictions || undefined, preferredWorkoutTime: fitnessProfile.preferredWorkoutTime as | "morning" | "afternoon" | "evening", workoutFrequency: parseInt(fitnessProfile.workoutFrequency) || 3, }; await fitnessProfileApi.createFitnessProfile(fitnessData, token); router.replace("/(tabs)"); } catch (error) { console.error("Error creating fitness profile:", error); Alert.alert( "Error", "Failed to create fitness profile. Please try again.", ); } finally { setIsSubmitting(false); } }; const handleLevelSelect = (level: string) => { setFitnessProfile({ ...fitnessProfile, fitnessLevel: level }); }; const handleTimeSelect = (time: string) => { setFitnessProfile({ ...fitnessProfile, preferredWorkoutTime: time }); }; return ( Set Up Your Fitness Profile Help us personalize your fitness journey Height (cm) setFitnessProfile({ ...fitnessProfile, height: value }) } keyboardType="numeric" placeholder="Enter height in cm" /> Weight (kg) setFitnessProfile({ ...fitnessProfile, weight: value }) } keyboardType="numeric" placeholder="Enter weight in kg" /> Fitness Goals setFitnessProfile({ ...fitnessProfile, goals: value }) } multiline numberOfLines={3} placeholder="What are your fitness goals?" /> Fitness Level {["beginner", "intermediate", "advanced"].map((level) => ( handleLevelSelect(level)} > {level.charAt(0).toUpperCase() + level.slice(1)} ))} Medical Conditions setFitnessProfile({ ...fitnessProfile, medicalConditions: value }) } multiline numberOfLines={3} placeholder="Any medical conditions we should know about?" /> Dietary Restrictions setFitnessProfile({ ...fitnessProfile, dietaryRestrictions: value, }) } multiline numberOfLines={3} placeholder="Any dietary restrictions?" /> Preferred Workout Time {["morning", "afternoon", "evening"].map((time) => ( handleTimeSelect(time)} > {time.charAt(0).toUpperCase() + time.slice(1)} ))} Workouts per Week setFitnessProfile({ ...fitnessProfile, workoutFrequency: value }) } keyboardType="numeric" placeholder="Number of workouts per week" /> Select a Gym {gymsLoading ? ( ) : ( setSelectedGymId(null)} > Proceed without gym {gyms.map((gym) => ( setSelectedGymId(gym.id)} > {gym.name} ))} )} {isSubmitting ? ( ) : ( Complete Setup )} ); } const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: "#f5f5f5", }, title: { fontSize: 24, fontWeight: "bold", textAlign: "center", marginTop: 40, marginBottom: 8, }, subtitle: { fontSize: 16, color: "#666", textAlign: "center", marginBottom: 32, }, form: { padding: 20, }, label: { fontSize: 14, fontWeight: "600", color: "#374151", marginBottom: 4, }, input: { borderWidth: 1, borderColor: "#ddd", borderRadius: 8, padding: 12, marginBottom: 16, backgroundColor: "white", }, textArea: { height: 80, textAlignVertical: "top", }, buttonGroup: { flexDirection: "row", justifyContent: "space-between", marginBottom: 16, }, levelButton: { flex: 1, backgroundColor: "#f3f4f6", padding: 10, borderRadius: 8, marginHorizontal: 4, alignItems: "center", }, timeButton: { flex: 1, backgroundColor: "#f3f4f6", padding: 10, borderRadius: 8, marginHorizontal: 4, alignItems: "center", }, selectedButton: { backgroundColor: "#3b82f6", }, levelButtonText: { color: "#374151", fontSize: 14, fontWeight: "500", }, timeButtonText: { color: "#374151", fontSize: 14, fontWeight: "500", }, selectedButtonText: { color: "white", }, submitButton: { backgroundColor: "#3b82f6", padding: 16, borderRadius: 8, alignItems: "center", marginTop: 24, }, submitButtonText: { color: "white", fontSize: 16, fontWeight: "600", }, });