fix with stackview

This commit is contained in:
echo 2025-11-26 03:10:39 +01:00
parent c038a54f2e
commit 06237579f0
2 changed files with 327 additions and 321 deletions

View File

@ -10,7 +10,7 @@ import {
TextInput, TextInput,
Platform, Platform,
} from 'react-native'; } from 'react-native';
import { useRouter } from 'expo-router'; import { useRouter, Stack } from 'expo-router';
import { useAuth } from '@clerk/clerk-expo'; import { useAuth } from '@clerk/clerk-expo';
import { Ionicons } from '@expo/vector-icons'; import { Ionicons } from '@expo/vector-icons';
import { LinearGradient } from 'expo-linear-gradient'; import { LinearGradient } from 'expo-linear-gradient';
@ -156,228 +156,231 @@ export default function FitnessProfileScreen() {
} }
return ( return (
<View style={styles.container}> <>
{/* Header */} <Stack.Screen options={{ headerShown: false }} />
<LinearGradient colors={theme.gradients.primary} style={styles.header}> <View style={styles.container}>
<TouchableOpacity style={styles.backButton} onPress={() => router.back()}> {/* Header */}
<Ionicons name="arrow-back" size={24} color="#fff" /> <LinearGradient colors={theme.gradients.primary} style={styles.header}>
</TouchableOpacity> <TouchableOpacity style={styles.backButton} onPress={() => router.back()}>
<Text style={styles.headerTitle}>Fitness Profile</Text> <Ionicons name="arrow-back" size={24} color="#fff" />
<View style={{ width: 40 }} /> </TouchableOpacity>
</LinearGradient> <Text style={styles.headerTitle}>Fitness Profile</Text>
<View style={{ width: 40 }} />
</LinearGradient>
<ScrollView <ScrollView
style={styles.scrollView} style={styles.scrollView}
contentContainerStyle={styles.scrollContent} contentContainerStyle={styles.scrollContent}
showsVerticalScrollIndicator={false} showsVerticalScrollIndicator={false}
>
{/* Basic Information */}
<View style={styles.section}>
<Text style={styles.sectionTitle}>Basic Information</Text>
<View style={styles.card}>
<View style={styles.row}>
<View style={styles.inputGroup}>
<Text style={styles.label}>Height (cm)</Text>
<View style={styles.inputContainer}>
<Ionicons name="resize-outline" size={20} color={theme.colors.gray400} />
<TextInput
style={styles.input}
value={profileData.height?.toString() || ''}
onChangeText={(text) =>
updateField('height', text ? parseFloat(text) : undefined)
}
keyboardType="decimal-pad"
placeholder="175"
placeholderTextColor={theme.colors.gray400}
/>
</View>
</View>
<View style={styles.inputGroup}>
<Text style={styles.label}>Weight (kg)</Text>
<View style={styles.inputContainer}>
<Ionicons name="scale-outline" size={20} color={theme.colors.gray400} />
<TextInput
style={styles.input}
value={profileData.weight?.toString() || ''}
onChangeText={(text) =>
updateField('weight', text ? parseFloat(text) : undefined)
}
keyboardType="decimal-pad"
placeholder="70"
placeholderTextColor={theme.colors.gray400}
/>
</View>
</View>
</View>
<View style={styles.inputGroup}>
<Text style={styles.label}>Age</Text>
<View style={styles.inputContainer}>
<Ionicons name="calendar-outline" size={20} color={theme.colors.gray400} />
<TextInput
style={styles.input}
value={profileData.age?.toString() || ''}
onChangeText={(text) =>
updateField('age', text ? parseInt(text, 10) : undefined)
}
keyboardType="number-pad"
placeholder="25"
placeholderTextColor={theme.colors.gray400}
/>
</View>
</View>
</View>
</View>
{/* Gender Selection */}
<View style={styles.section}>
<Text style={styles.sectionTitle}>Gender</Text>
<View style={styles.optionsRow}>
{GENDER_OPTIONS.map((option) => (
<TouchableOpacity
key={option.value}
style={[
styles.optionCard,
profileData.gender === option.value && styles.optionCardActive,
]}
onPress={() => updateField('gender', option.value)}
>
<Ionicons
name={option.icon as any}
size={24}
color={
profileData.gender === option.value
? theme.colors.primary
: theme.colors.gray400
}
/>
<Text
style={[
styles.optionLabel,
profileData.gender === option.value && styles.optionLabelActive,
]}
>
{option.label}
</Text>
</TouchableOpacity>
))}
</View>
</View>
{/* Fitness Goal */}
<View style={styles.section}>
<Text style={styles.sectionTitle}>Primary Fitness Goal</Text>
<View style={styles.card}>
{FITNESS_GOAL_OPTIONS.map((option, index) => (
<React.Fragment key={option.value}>
<TouchableOpacity
style={styles.listItem}
onPress={() => updateField('fitnessGoal', option.value)}
>
<View style={[styles.iconCircle, { backgroundColor: `${option.color}20` }]}>
<Ionicons name={option.icon as any} size={20} color={option.color} />
</View>
<Text style={styles.listItemText}>{option.label}</Text>
{profileData.fitnessGoal === option.value && (
<Ionicons name="checkmark-circle" size={24} color={theme.colors.primary} />
)}
</TouchableOpacity>
{index < FITNESS_GOAL_OPTIONS.length - 1 && <View style={styles.divider} />}
</React.Fragment>
))}
</View>
</View>
{/* Activity Level */}
<View style={styles.section}>
<Text style={styles.sectionTitle}>Activity Level</Text>
<View style={styles.card}>
{ACTIVITY_LEVEL_OPTIONS.map((option, index) => (
<React.Fragment key={option.value}>
<TouchableOpacity
style={styles.listItem}
onPress={() => updateField('activityLevel', option.value)}
>
<View style={{ flex: 1 }}>
<Text style={styles.listItemText}>{option.label}</Text>
<Text style={styles.listItemDescription}>{option.description}</Text>
</View>
{profileData.activityLevel === option.value && (
<Ionicons name="checkmark-circle" size={24} color={theme.colors.primary} />
)}
</TouchableOpacity>
{index < ACTIVITY_LEVEL_OPTIONS.length - 1 && <View style={styles.divider} />}
</React.Fragment>
))}
</View>
</View>
{/* Health Information */}
<View style={styles.section}>
<Text style={styles.sectionTitle}>Health Information (Optional)</Text>
<View style={styles.card}>
<View style={styles.inputGroup}>
<Text style={styles.label}>Medical Conditions</Text>
<TextInput
style={[styles.textArea]}
value={profileData.medicalConditions || ''}
onChangeText={(text) => updateField('medicalConditions', text)}
placeholder="e.g., Asthma, diabetes..."
placeholderTextColor={theme.colors.gray400}
multiline
numberOfLines={3}
textAlignVertical="top"
/>
</View>
<View style={styles.inputGroup}>
<Text style={styles.label}>Allergies</Text>
<TextInput
style={[styles.textArea]}
value={profileData.allergies || ''}
onChangeText={(text) => updateField('allergies', text)}
placeholder="e.g., Peanuts, latex..."
placeholderTextColor={theme.colors.gray400}
multiline
numberOfLines={3}
textAlignVertical="top"
/>
</View>
<View style={styles.inputGroup}>
<Text style={styles.label}>Injuries</Text>
<TextInput
style={[styles.textArea]}
value={profileData.injuries || ''}
onChangeText={(text) => updateField('injuries', text)}
placeholder="e.g., Previous knee injury..."
placeholderTextColor={theme.colors.gray400}
multiline
numberOfLines={3}
textAlignVertical="top"
/>
</View>
</View>
</View>
</ScrollView>
{/* Save Button */}
<View style={styles.footer}>
<TouchableOpacity
style={[styles.saveButton, loading && styles.saveButtonDisabled]}
onPress={handleSave}
disabled={loading}
> >
<LinearGradient colors={theme.gradients.primary} style={styles.saveButtonGradient}> {/* Basic Information */}
{loading ? ( <View style={styles.section}>
<ActivityIndicator color="#fff" /> <Text style={styles.sectionTitle}>Basic Information</Text>
) : ( <View style={styles.card}>
<> <View style={styles.row}>
<Ionicons name="checkmark-circle" size={20} color="#fff" /> <View style={styles.inputGroup}>
<Text style={styles.saveButtonText}>Save Profile</Text> <Text style={styles.label}>Height (cm)</Text>
</> <View style={styles.inputContainer}>
)} <Ionicons name="resize-outline" size={20} color={theme.colors.gray400} />
</LinearGradient> <TextInput
</TouchableOpacity> style={styles.input}
value={profileData.height?.toString() || ''}
onChangeText={(text) =>
updateField('height', text ? parseFloat(text) : undefined)
}
keyboardType="decimal-pad"
placeholder="175"
placeholderTextColor={theme.colors.gray400}
/>
</View>
</View>
<View style={styles.inputGroup}>
<Text style={styles.label}>Weight (kg)</Text>
<View style={styles.inputContainer}>
<Ionicons name="scale-outline" size={20} color={theme.colors.gray400} />
<TextInput
style={styles.input}
value={profileData.weight?.toString() || ''}
onChangeText={(text) =>
updateField('weight', text ? parseFloat(text) : undefined)
}
keyboardType="decimal-pad"
placeholder="70"
placeholderTextColor={theme.colors.gray400}
/>
</View>
</View>
</View>
<View style={styles.inputGroup}>
<Text style={styles.label}>Age</Text>
<View style={styles.inputContainer}>
<Ionicons name="calendar-outline" size={20} color={theme.colors.gray400} />
<TextInput
style={styles.input}
value={profileData.age?.toString() || ''}
onChangeText={(text) =>
updateField('age', text ? parseInt(text, 10) : undefined)
}
keyboardType="number-pad"
placeholder="25"
placeholderTextColor={theme.colors.gray400}
/>
</View>
</View>
</View>
</View>
{/* Gender Selection */}
<View style={styles.section}>
<Text style={styles.sectionTitle}>Gender</Text>
<View style={styles.optionsRow}>
{GENDER_OPTIONS.map((option) => (
<TouchableOpacity
key={option.value}
style={[
styles.optionCard,
profileData.gender === option.value && styles.optionCardActive,
]}
onPress={() => updateField('gender', option.value)}
>
<Ionicons
name={option.icon as any}
size={24}
color={
profileData.gender === option.value
? theme.colors.primary
: theme.colors.gray400
}
/>
<Text
style={[
styles.optionLabel,
profileData.gender === option.value && styles.optionLabelActive,
]}
>
{option.label}
</Text>
</TouchableOpacity>
))}
</View>
</View>
{/* Fitness Goal */}
<View style={styles.section}>
<Text style={styles.sectionTitle}>Primary Fitness Goal</Text>
<View style={styles.card}>
{FITNESS_GOAL_OPTIONS.map((option, index) => (
<React.Fragment key={option.value}>
<TouchableOpacity
style={styles.listItem}
onPress={() => updateField('fitnessGoal', option.value)}
>
<View style={[styles.iconCircle, { backgroundColor: `${option.color}20` }]}>
<Ionicons name={option.icon as any} size={20} color={option.color} />
</View>
<Text style={styles.listItemText}>{option.label}</Text>
{profileData.fitnessGoal === option.value && (
<Ionicons name="checkmark-circle" size={24} color={theme.colors.primary} />
)}
</TouchableOpacity>
{index < FITNESS_GOAL_OPTIONS.length - 1 && <View style={styles.divider} />}
</React.Fragment>
))}
</View>
</View>
{/* Activity Level */}
<View style={styles.section}>
<Text style={styles.sectionTitle}>Activity Level</Text>
<View style={styles.card}>
{ACTIVITY_LEVEL_OPTIONS.map((option, index) => (
<React.Fragment key={option.value}>
<TouchableOpacity
style={styles.listItem}
onPress={() => updateField('activityLevel', option.value)}
>
<View style={{ flex: 1 }}>
<Text style={styles.listItemText}>{option.label}</Text>
<Text style={styles.listItemDescription}>{option.description}</Text>
</View>
{profileData.activityLevel === option.value && (
<Ionicons name="checkmark-circle" size={24} color={theme.colors.primary} />
)}
</TouchableOpacity>
{index < ACTIVITY_LEVEL_OPTIONS.length - 1 && <View style={styles.divider} />}
</React.Fragment>
))}
</View>
</View>
{/* Health Information */}
<View style={styles.section}>
<Text style={styles.sectionTitle}>Health Information (Optional)</Text>
<View style={styles.card}>
<View style={styles.inputGroup}>
<Text style={styles.label}>Medical Conditions</Text>
<TextInput
style={[styles.textArea]}
value={profileData.medicalConditions || ''}
onChangeText={(text) => updateField('medicalConditions', text)}
placeholder="e.g., Asthma, diabetes..."
placeholderTextColor={theme.colors.gray400}
multiline
numberOfLines={3}
textAlignVertical="top"
/>
</View>
<View style={styles.inputGroup}>
<Text style={styles.label}>Allergies</Text>
<TextInput
style={[styles.textArea]}
value={profileData.allergies || ''}
onChangeText={(text) => updateField('allergies', text)}
placeholder="e.g., Peanuts, latex..."
placeholderTextColor={theme.colors.gray400}
multiline
numberOfLines={3}
textAlignVertical="top"
/>
</View>
<View style={styles.inputGroup}>
<Text style={styles.label}>Injuries</Text>
<TextInput
style={[styles.textArea]}
value={profileData.injuries || ''}
onChangeText={(text) => updateField('injuries', text)}
placeholder="e.g., Previous knee injury..."
placeholderTextColor={theme.colors.gray400}
multiline
numberOfLines={3}
textAlignVertical="top"
/>
</View>
</View>
</View>
</ScrollView>
{/* Save Button */}
<View style={styles.footer}>
<TouchableOpacity
style={[styles.saveButton, loading && styles.saveButtonDisabled]}
onPress={handleSave}
disabled={loading}
>
<LinearGradient colors={theme.gradients.primary} style={styles.saveButtonGradient}>
{loading ? (
<ActivityIndicator color="#fff" />
) : (
<>
<Ionicons name="checkmark-circle" size={20} color="#fff" />
<Text style={styles.saveButtonText}>Save Profile</Text>
</>
)}
</LinearGradient>
</TouchableOpacity>
</View>
</View> </View>
</View> </>
); );
} }

View File

@ -9,7 +9,7 @@ import {
Alert, Alert,
Platform, Platform,
} from 'react-native'; } from 'react-native';
import { useRouter } from 'expo-router'; import { useRouter, Stack } from 'expo-router';
import { useUser } from '@clerk/clerk-expo'; import { useUser } from '@clerk/clerk-expo';
import { Ionicons } from '@expo/vector-icons'; import { Ionicons } from '@expo/vector-icons';
import { LinearGradient } from 'expo-linear-gradient'; import { LinearGradient } from 'expo-linear-gradient';
@ -53,109 +53,112 @@ export default function PersonalDetailsScreen() {
}; };
return ( return (
<View style={styles.container}> <>
{/* Header */} <Stack.Screen options={{ headerShown: false }} />
<LinearGradient <View style={styles.container}>
colors={theme.gradients.primary} {/* Header */}
style={styles.header} <LinearGradient
> colors={theme.gradients.primary}
<TouchableOpacity style={styles.header}
style={styles.backButton}
onPress={() => router.back()}
> >
<Ionicons name="arrow-back" size={24} color="#fff" /> <TouchableOpacity
</TouchableOpacity> style={styles.backButton}
<Text style={styles.headerTitle}>Personal Details</Text> onPress={() => router.back()}
<View style={{ width: 40 }} />
</LinearGradient>
<ScrollView
style={styles.content}
contentContainerStyle={styles.scrollContent}
showsVerticalScrollIndicator={false}
>
{/* First Name */}
<View style={styles.field}>
<Text style={styles.label}>First Name *</Text>
<View style={styles.inputContainer}>
<Ionicons name="person-outline" size={20} color={theme.colors.gray400} style={styles.inputIcon} />
<TextInput
style={styles.input}
value={formData.firstName}
onChangeText={(value) => updateField('firstName', value)}
placeholder="Enter first name"
placeholderTextColor={theme.colors.gray400}
/>
</View>
</View>
{/* Last Name */}
<View style={styles.field}>
<Text style={styles.label}>Last Name *</Text>
<View style={styles.inputContainer}>
<Ionicons name="person-outline" size={20} color={theme.colors.gray400} style={styles.inputIcon} />
<TextInput
style={styles.input}
value={formData.lastName}
onChangeText={(value) => updateField('lastName', value)}
placeholder="Enter last name"
placeholderTextColor={theme.colors.gray400}
/>
</View>
</View>
{/* Email (Read-only) */}
<View style={styles.field}>
<Text style={styles.label}>Email</Text>
<View style={[styles.inputContainer, styles.disabledInput]}>
<Ionicons name="mail-outline" size={20} color={theme.colors.gray400} style={styles.inputIcon} />
<TextInput
style={[styles.input, styles.disabledText]}
value={formData.email}
editable={false}
placeholderTextColor={theme.colors.gray400}
/>
<Ionicons name="lock-closed-outline" size={16} color={theme.colors.gray400} />
</View>
<Text style={styles.helperText}>Email cannot be changed here</Text>
</View>
{/* Phone (Read-only for now) */}
<View style={styles.field}>
<Text style={styles.label}>Phone Number</Text>
<View style={[styles.inputContainer, styles.disabledInput]}>
<Ionicons name="call-outline" size={20} color={theme.colors.gray400} style={styles.inputIcon} />
<TextInput
style={[styles.input, styles.disabledText]}
value={formData.phone || 'Not set'}
editable={false}
placeholderTextColor={theme.colors.gray400}
/>
<Ionicons name="lock-closed-outline" size={16} color={theme.colors.gray400} />
</View>
<Text style={styles.helperText}>Phone number cannot be changed here</Text>
</View>
</ScrollView>
{/* Save Button */}
<View style={styles.footer}>
<TouchableOpacity
style={[styles.saveButton, loading && styles.saveButtonDisabled]}
onPress={handleSave}
disabled={loading}
>
<LinearGradient
colors={theme.gradients.primary}
style={styles.saveButtonGradient}
> >
<Ionicons name="checkmark-circle" size={20} color="#fff" /> <Ionicons name="arrow-back" size={24} color="#fff" />
<Text style={styles.saveButtonText}> </TouchableOpacity>
{loading ? 'Saving...' : 'Save Changes'} <Text style={styles.headerTitle}>Personal Details</Text>
</Text> <View style={{ width: 40 }} />
</LinearGradient> </LinearGradient>
</TouchableOpacity>
<ScrollView
style={styles.content}
contentContainerStyle={styles.scrollContent}
showsVerticalScrollIndicator={false}
>
{/* First Name */}
<View style={styles.field}>
<Text style={styles.label}>First Name *</Text>
<View style={styles.inputContainer}>
<Ionicons name="person-outline" size={20} color={theme.colors.gray400} style={styles.inputIcon} />
<TextInput
style={styles.input}
value={formData.firstName}
onChangeText={(value) => updateField('firstName', value)}
placeholder="Enter first name"
placeholderTextColor={theme.colors.gray400}
/>
</View>
</View>
{/* Last Name */}
<View style={styles.field}>
<Text style={styles.label}>Last Name *</Text>
<View style={styles.inputContainer}>
<Ionicons name="person-outline" size={20} color={theme.colors.gray400} style={styles.inputIcon} />
<TextInput
style={styles.input}
value={formData.lastName}
onChangeText={(value) => updateField('lastName', value)}
placeholder="Enter last name"
placeholderTextColor={theme.colors.gray400}
/>
</View>
</View>
{/* Email (Read-only) */}
<View style={styles.field}>
<Text style={styles.label}>Email</Text>
<View style={[styles.inputContainer, styles.disabledInput]}>
<Ionicons name="mail-outline" size={20} color={theme.colors.gray400} style={styles.inputIcon} />
<TextInput
style={[styles.input, styles.disabledText]}
value={formData.email}
editable={false}
placeholderTextColor={theme.colors.gray400}
/>
<Ionicons name="lock-closed-outline" size={16} color={theme.colors.gray400} />
</View>
<Text style={styles.helperText}>Email cannot be changed here</Text>
</View>
{/* Phone (Read-only for now) */}
<View style={styles.field}>
<Text style={styles.label}>Phone Number</Text>
<View style={[styles.inputContainer, styles.disabledInput]}>
<Ionicons name="call-outline" size={20} color={theme.colors.gray400} style={styles.inputIcon} />
<TextInput
style={[styles.input, styles.disabledText]}
value={formData.phone || 'Not set'}
editable={false}
placeholderTextColor={theme.colors.gray400}
/>
<Ionicons name="lock-closed-outline" size={16} color={theme.colors.gray400} />
</View>
<Text style={styles.helperText}>Phone number cannot be changed here</Text>
</View>
</ScrollView>
{/* Save Button */}
<View style={styles.footer}>
<TouchableOpacity
style={[styles.saveButton, loading && styles.saveButtonDisabled]}
onPress={handleSave}
disabled={loading}
>
<LinearGradient
colors={theme.gradients.primary}
style={styles.saveButtonGradient}
>
<Ionicons name="checkmark-circle" size={20} color="#fff" />
<Text style={styles.saveButtonText}>
{loading ? 'Saving...' : 'Save Changes'}
</Text>
</LinearGradient>
</TouchableOpacity>
</View>
</View> </View>
</View> </>
); );
} }