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"; import { gymsApi, type Gym } from "@/api/gyms"; import log from "../../utils/logger"; export default function OnboardingScreen() { const { user } = useUser(); const { getToken } = useAuth(); const router = useRouter(); const [isSubmitting, setIsSubmitting] = useState(false); const [gyms, setGyms] = useState([]); const [gymsLoading, setGymsLoading] = useState(false); const [selectedGymId, setSelectedGymId] = useState(null); useEffect(() => { const loadGyms = async () => { try { setGymsLoading(true); const token = await getToken(); const data = await gymsApi.getGyms(token); setGyms(data); } catch (e) { log.error("Failed to fetch gyms", e); } finally { setGymsLoading(false); } }; loadGyms(); }, []); const [fitnessProfile, setFitnessProfile] = useState({ height: "", weight: "", age: "", 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 || !fitnessProfile.age ) { Alert.alert("Error", "Please enter your height, weight, and age"); 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 gymsApi.updateUserGym(selectedGymId, token); } catch (e) { log.warn("Failed to update gym selection", { gymId: selectedGymId }); } const fitnessData = { userId: user.id, height: fitnessProfile.height ? parseFloat(fitnessProfile.height) : undefined, weight: fitnessProfile.weight ? parseFloat(fitnessProfile.weight) : undefined, age: fitnessProfile.age ? parseInt(fitnessProfile.age, 10) : undefined, fitnessGoals: fitnessProfile.goals ? [fitnessProfile.goals] : [], medicalConditions: fitnessProfile.medicalConditions || undefined, allergies: fitnessProfile.dietaryRestrictions || undefined, }; await fitnessProfileApi.createFitnessProfile(fitnessData, token); router.replace("/(tabs)"); } catch (error) { log.error("Failed to create 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 }); }; // Calculate progress based on filled fields const calculateProgress = () => { const fields = [ fitnessProfile.height, fitnessProfile.weight, fitnessProfile.age, fitnessProfile.goals, fitnessProfile.medicalConditions || "n/a", // Optional fields count as filled fitnessProfile.dietaryRestrictions || "n/a", ]; const filledFields = fields.filter( (field) => field && field.trim() !== "", ).length; return Math.round((filledFields / fields.length) * 100); }; const progress = calculateProgress(); return ( Set Up Your Fitness Profile Help us personalize your fitness journey {/* Progress indicator */} {progress}% Complete 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" /> Age setFitnessProfile({ ...fitnessProfile, age: value }) } keyboardType="numeric" placeholder="Enter your age" /> 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", }, progressContainer: { paddingHorizontal: 20, marginBottom: 16, }, progressBarBackground: { height: 8, backgroundColor: "#e5e7eb", borderRadius: 4, overflow: "hidden", marginBottom: 8, }, progressBarFill: { height: "100%", backgroundColor: "#3b82f6", borderRadius: 4, }, progressText: { fontSize: 12, color: "#6b7280", textAlign: "center", }, });