normalize mobile auth and profile ui to theme components

This commit is contained in:
dimitar committed 2026-03-29 20:07:33 +02:00
1 parent 7ada05da6a
commit ed14c57749
7 files changed
+557 -615

No files matched your search

+151 -127
View File
@@ -13,12 +13,17 @@ 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 { useTheme } from "../../contexts/ThemeContext";
import { Input } from "../../components/Input";
import { MinimalButton } from "../../components/MinimalButton";
import { MinimalCard } from "../../components/MinimalCard";
import log from "../../utils/logger";
export default function OnboardingScreen() {
const { user } = useUser();
const { getToken } = useAuth();
const router = useRouter();
const { colors, typography } = useTheme();
const [isSubmitting, setIsSubmitting] = useState(false);
const [gyms, setGyms] = useState<Gym[]>([]);
const [gymsLoading, setGymsLoading] = useState<boolean>(false);
@@ -134,24 +139,53 @@ export default function OnboardingScreen() {
const progress = calculateProgress();
return (
<ScrollView style={styles.container}>
<Text style={styles.title}>Set Up Your Fitness Profile</Text>
<Text style={styles.subtitle}>
<ScrollView
style={[styles.container, { backgroundColor: colors.background }]}
>
<Text
style={[typography.h2, { color: colors.textPrimary }, styles.title]}
>
Set Up Your Fitness Profile
</Text>
<Text
style={[
typography.body,
{ color: colors.textSecondary },
styles.subtitle,
]}
>
Help us personalize your fitness journey
</Text>
{/* Progress indicator */}
<View style={styles.progressContainer}>
<View style={styles.progressBarBackground}>
<View style={[styles.progressBarFill, { width: `${progress}%` }]} />
<View
style={[
styles.progressBarBackground,
{ backgroundColor: colors.borderLight },
]}
>
<View
style={[
styles.progressBarFill,
{ width: `${progress}%`, backgroundColor: colors.primary },
]}
/>
</View>
<Text style={styles.progressText}>{progress}% Complete</Text>
<Text
style={[
typography.caption,
{ color: colors.textTertiary },
styles.progressText,
]}
>
{progress}% Complete
</Text>
</View>
<View style={styles.form}>
<Text style={styles.label}>Height (cm)</Text>
<TextInput
style={styles.input}
<Input
label="Height (cm)"
value={fitnessProfile.height}
onChangeText={(value) =>
setFitnessProfile({ ...fitnessProfile, height: value })
@@ -160,9 +194,8 @@ export default function OnboardingScreen() {
placeholder="Enter height in cm"
/>
<Text style={styles.label}>Weight (kg)</Text>
<TextInput
style={styles.input}
<Input
label="Weight (kg)"
value={fitnessProfile.weight}
onChangeText={(value) =>
setFitnessProfile({ ...fitnessProfile, weight: value })
@@ -171,9 +204,8 @@ export default function OnboardingScreen() {
placeholder="Enter weight in kg"
/>
<Text style={styles.label}>Age</Text>
<TextInput
style={styles.input}
<Input
label="Age"
value={fitnessProfile.age}
onChangeText={(value) =>
setFitnessProfile({ ...fitnessProfile, age: value })
@@ -182,9 +214,9 @@ export default function OnboardingScreen() {
placeholder="Enter your age"
/>
<Text style={styles.label}>Fitness Goals</Text>
<TextInput
style={[styles.input, styles.textArea]}
<Input
label="Fitness Goals"
style={styles.textArea}
value={fitnessProfile.goals}
onChangeText={(value) =>
setFitnessProfile({ ...fitnessProfile, goals: value })
@@ -194,33 +226,48 @@ export default function OnboardingScreen() {
placeholder="What are your fitness goals?"
/>
<Text style={styles.label}>Fitness Level</Text>
<Text
style={[typography.h4, { color: colors.textPrimary }, styles.label]}
>
Fitness Level
</Text>
<View style={styles.buttonGroup}>
{["beginner", "intermediate", "advanced"].map((level) => (
<TouchableOpacity
key={level}
style={[
styles.levelButton,
fitnessProfile.fitnessLevel === level && styles.selectedButton,
styles.segmentButton,
{
backgroundColor:
fitnessProfile.fitnessLevel === level
? colors.primary
: colors.surface,
borderColor: colors.border,
},
]}
onPress={() => handleLevelSelect(level)}
>
<Text
style={[
styles.levelButtonText,
fitnessProfile.fitnessLevel === level &&
styles.selectedButtonText,
typography.caption,
{
color:
fitnessProfile.fitnessLevel === level
? colors.white
: colors.textSecondary,
textTransform: "capitalize",
},
]}
>
{level.charAt(0).toUpperCase() + level.slice(1)}
{level}
</Text>
</TouchableOpacity>
))}
</View>
<Text style={styles.label}>Medical Conditions</Text>
<TextInput
style={[styles.input, styles.textArea]}
<Input
label="Medical Conditions"
style={styles.textArea}
value={fitnessProfile.medicalConditions}
onChangeText={(value) =>
setFitnessProfile({ ...fitnessProfile, medicalConditions: value })
@@ -230,9 +277,9 @@ export default function OnboardingScreen() {
placeholder="Any medical conditions we should know about?"
/>
<Text style={styles.label}>Dietary Restrictions</Text>
<TextInput
style={[styles.input, styles.textArea]}
<Input
label="Dietary Restrictions"
style={styles.textArea}
value={fitnessProfile.dietaryRestrictions}
onChangeText={(value) =>
setFitnessProfile({
@@ -245,34 +292,47 @@ export default function OnboardingScreen() {
placeholder="Any dietary restrictions?"
/>
<Text style={styles.label}>Preferred Workout Time</Text>
<Text
style={[typography.h4, { color: colors.textPrimary }, styles.label]}
>
Preferred Workout Time
</Text>
<View style={styles.buttonGroup}>
{["morning", "afternoon", "evening"].map((time) => (
<TouchableOpacity
key={time}
style={[
styles.timeButton,
fitnessProfile.preferredWorkoutTime === time &&
styles.selectedButton,
styles.segmentButton,
{
backgroundColor:
fitnessProfile.preferredWorkoutTime === time
? colors.primary
: colors.surface,
borderColor: colors.border,
},
]}
onPress={() => handleTimeSelect(time)}
>
<Text
style={[
styles.timeButtonText,
fitnessProfile.preferredWorkoutTime === time &&
styles.selectedButtonText,
typography.caption,
{
color:
fitnessProfile.preferredWorkoutTime === time
? colors.white
: colors.textSecondary,
textTransform: "capitalize",
},
]}
>
{time.charAt(0).toUpperCase() + time.slice(1)}
{time}
</Text>
</TouchableOpacity>
))}
</View>
<Text style={styles.label}>Workouts per Week</Text>
<TextInput
style={styles.input}
<Input
label="Workouts per Week"
value={fitnessProfile.workoutFrequency}
onChangeText={(value) =>
setFitnessProfile({ ...fitnessProfile, workoutFrequency: value })
@@ -281,7 +341,11 @@ export default function OnboardingScreen() {
placeholder="Number of workouts per week"
/>
<Text style={styles.label}>Select a Gym</Text>
<Text
style={[typography.h4, { color: colors.textPrimary }, styles.label]}
>
Select a Gym
</Text>
{gymsLoading ? (
<ActivityIndicator />
) : (
@@ -293,15 +357,24 @@ export default function OnboardingScreen() {
<View style={{ flexDirection: "row" }}>
<TouchableOpacity
style={[
styles.levelButton,
selectedGymId === null && styles.selectedButton,
styles.segmentButton,
{
backgroundColor:
selectedGymId === null ? colors.primary : colors.surface,
borderColor: colors.border,
},
]}
onPress={() => setSelectedGymId(null)}
>
<Text
style={[
styles.levelButtonText,
selectedGymId === null && styles.selectedButtonText,
typography.caption,
{
color:
selectedGymId === null
? colors.white
: colors.textSecondary,
},
]}
>
Proceed without gym
@@ -311,15 +384,26 @@ export default function OnboardingScreen() {
<TouchableOpacity
key={gym.id}
style={[
styles.levelButton,
selectedGymId === gym.id && styles.selectedButton,
styles.segmentButton,
{
backgroundColor:
selectedGymId === gym.id
? colors.primary
: colors.surface,
borderColor: colors.border,
},
]}
onPress={() => setSelectedGymId(gym.id)}
>
<Text
style={[
styles.levelButtonText,
selectedGymId === gym.id && styles.selectedButtonText,
typography.caption,
{
color:
selectedGymId === gym.id
? colors.white
: colors.textSecondary,
},
]}
>
{gym.name}
@@ -330,17 +414,14 @@ export default function OnboardingScreen() {
</ScrollView>
)}
<TouchableOpacity
style={styles.submitButton}
<MinimalButton
title="Complete Setup"
onPress={handleSubmit}
loading={isSubmitting}
disabled={isSubmitting}
>
{isSubmitting ? (
<ActivityIndicator color="white" />
) : (
<Text style={styles.submitButtonText}>Complete Setup</Text>
)}
</TouchableOpacity>
fullWidth
size="lg"
/>
</View>
</ScrollView>
);
@@ -349,18 +430,13 @@ export default function OnboardingScreen() {
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,
},
@@ -368,91 +444,39 @@ const styles = StyleSheet.create({
padding: 20,
},
label: {
fontSize: 14,
fontWeight: "600",
color: "#374151",
marginBottom: 4,
},
input: {
borderWidth: 1,
borderColor: "#ddd",
borderRadius: 8,
padding: 12,
marginBottom: 16,
backgroundColor: "white",
marginBottom: 8,
},
textArea: {
height: 80,
minHeight: 80,
textAlignVertical: "top",
marginBottom: 16,
},
buttonGroup: {
flexDirection: "row",
justifyContent: "space-between",
flexWrap: "wrap",
gap: 8,
marginBottom: 16,
},
levelButton: {
flex: 1,
backgroundColor: "#f3f4f6",
segmentButton: {
minWidth: 100,
padding: 10,
borderRadius: 8,
marginHorizontal: 4,
borderRadius: 10,
borderWidth: 1,
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",
},
progressText: { textAlign: "center" },
});