diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000..61ef9aa16c Binary files /dev/null and b/.DS_Store differ diff --git a/.env b/.env index 1d44286e25..41fd7697c6 100644 --- a/.env +++ b/.env @@ -13,14 +13,14 @@ FRONTEND_HOST=http://localhost:5173 # Environment: local, staging, production ENVIRONMENT=local -PROJECT_NAME="Full Stack FastAPI Project" -STACK_NAME=full-stack-fastapi-project +PROJECT_NAME="Kondition Fastapi" +STACK_NAME=kondition-fastapi # Backend BACKEND_CORS_ORIGINS="http://localhost,http://localhost:5173,https://localhost,https://localhost:5173,http://localhost.tiangolo.com" -SECRET_KEY=changethis +SECRET_KEY=S8T75_i6lcsZerDMe59A9tmyx0-AtFAzGY5bez_dsBw FIRST_SUPERUSER=admin@example.com -FIRST_SUPERUSER_PASSWORD=changethis +FIRST_SUPERUSER_PASSWORD=VSOhm4DWGG8h1kOBAhlZYM0HqqL--r4CuAhZ-eXQX8Q # Emails SMTP_HOST= @@ -34,9 +34,9 @@ SMTP_PORT=587 # Postgres POSTGRES_SERVER=localhost POSTGRES_PORT=5432 -POSTGRES_DB=app +POSTGRES_DB=konditionDB POSTGRES_USER=postgres -POSTGRES_PASSWORD=changethis +POSTGRES_PASSWORD=d0pFlN6jcym0Fui_lc4dShkzzRAYUL78ToQTMoALE1g SENTRY_DSN= diff --git a/.expo/README.md b/.expo/README.md new file mode 100644 index 0000000000..fd146b4d3a --- /dev/null +++ b/.expo/README.md @@ -0,0 +1,15 @@ +> Why do I have a folder named ".expo" in my project? + +The ".expo" folder is created when an Expo project is started using "expo start" command. + +> What do the files contain? + +- "devices.json": contains information about devices that have recently opened this project. This is used to populate the "Development sessions" list in your development builds. +- "packager-info.json": contains port numbers and process PIDs that are used to serve the application to the mobile device/simulator. +- "settings.json": contains the server configuration that is used to serve the application manifest. + +> Should I commit the ".expo" folder? + +No, you should not share the ".expo" folder. It does not contain any information that is relevant for other developers working on the project, it is specific to your machine. + +Upon project creation, the ".expo" folder is already added to your ".gitignore" file. diff --git a/.expo/settings.json b/.expo/settings.json new file mode 100644 index 0000000000..92bc513bfd --- /dev/null +++ b/.expo/settings.json @@ -0,0 +1,8 @@ +{ + "hostType": "lan", + "lanType": "ip", + "dev": true, + "minify": false, + "urlRandomness": null, + "https": false +} diff --git a/KonditionExpo/.gitignore b/KonditionExpo/.gitignore new file mode 100644 index 0000000000..f610ec0d64 --- /dev/null +++ b/KonditionExpo/.gitignore @@ -0,0 +1,39 @@ +# Learn more https://docs.github.com/en/get-started/getting-started-with-git/ignoring-files + +# dependencies +node_modules/ + +# Expo +.expo/ +dist/ +web-build/ +expo-env.d.ts + +# Native +.kotlin/ +*.orig.* +*.jks +*.p8 +*.p12 +*.key +*.mobileprovision + +# Metro +.metro-health-check* + +# debug +npm-debug.* +yarn-debug.* +yarn-error.* + +# macOS +.DS_Store +*.pem + +# local env files +.env*.local + +# typescript +*.tsbuildinfo + +app-example diff --git a/KonditionExpo/README.md b/KonditionExpo/README.md new file mode 100644 index 0000000000..48dd63ff3e --- /dev/null +++ b/KonditionExpo/README.md @@ -0,0 +1,50 @@ +# Welcome to your Expo app 👋 + +This is an [Expo](https://expo.dev) project created with [`create-expo-app`](https://www.npmjs.com/package/create-expo-app). + +## Get started + +1. Install dependencies + + ```bash + npm install + ``` + +2. Start the app + + ```bash + npx expo start + ``` + +In the output, you'll find options to open the app in a + +- [development build](https://docs.expo.dev/develop/development-builds/introduction/) +- [Android emulator](https://docs.expo.dev/workflow/android-studio-emulator/) +- [iOS simulator](https://docs.expo.dev/workflow/ios-simulator/) +- [Expo Go](https://expo.dev/go), a limited sandbox for trying out app development with Expo + +You can start developing by editing the files inside the **app** directory. This project uses [file-based routing](https://docs.expo.dev/router/introduction). + +## Get a fresh project + +When you're ready, run: + +```bash +npm run reset-project +``` + +This command will move the starter code to the **app-example** directory and create a blank **app** directory where you can start developing. + +## Learn more + +To learn more about developing your project with Expo, look at the following resources: + +- [Expo documentation](https://docs.expo.dev/): Learn fundamentals, or go into advanced topics with our [guides](https://docs.expo.dev/guides). +- [Learn Expo tutorial](https://docs.expo.dev/tutorial/introduction/): Follow a step-by-step tutorial where you'll create a project that runs on Android, iOS, and the web. + +## Join the community + +Join our community of developers creating universal apps. + +- [Expo on GitHub](https://github.com/expo/expo): View our open source platform and contribute. +- [Discord community](https://chat.expo.dev): Chat with Expo users and ask questions. diff --git a/KonditionExpo/app.json b/KonditionExpo/app.json new file mode 100644 index 0000000000..a98dd8b7af --- /dev/null +++ b/KonditionExpo/app.json @@ -0,0 +1,42 @@ +{ + "expo": { + "name": "KonditionExpo", + "slug": "KonditionExpo", + "version": "1.0.0", + "orientation": "portrait", + "icon": "./assets/images/icon.png", + "scheme": "konditionexpo", + "userInterfaceStyle": "automatic", + "newArchEnabled": true, + "ios": { + "supportsTablet": true + }, + "android": { + "adaptiveIcon": { + "foregroundImage": "./assets/images/adaptive-icon.png", + "backgroundColor": "#ffffff" + }, + "edgeToEdgeEnabled": true + }, + "web": { + "bundler": "metro", + "output": "static", + "favicon": "./assets/images/favicon.png" + }, + "plugins": [ + "expo-router", + [ + "expo-splash-screen", + { + "image": "./assets/images/splash-icon.png", + "imageWidth": 200, + "resizeMode": "contain", + "backgroundColor": "#ffffff" + } + ] + ], + "experiments": { + "typedRoutes": true + } + } +} diff --git a/KonditionExpo/app/(tabs)/_layout.tsx b/KonditionExpo/app/(tabs)/_layout.tsx new file mode 100644 index 0000000000..a46fe0ad16 --- /dev/null +++ b/KonditionExpo/app/(tabs)/_layout.tsx @@ -0,0 +1,67 @@ +// app/(tabs)/_layout.tsx +import { Tabs, Redirect } from 'expo-router'; +import { useEffect } from 'react'; +import { ActivityIndicator, View } from 'react-native'; +import { Platform } from 'react-native'; + +import { useAuth } from '@/contexts/AuthContext'; +import { Colors } from '@/constants/Colors'; +import { useColorScheme } from '@/hooks/useColorScheme'; +import { HapticTab } from '@/components/HapticTab'; +import { IconSymbol } from '@/components/ui/IconSymbol'; +import TabBarBackground from '@/components/ui/TabBarBackground'; + +export default function TabLayout() { + const { token, loading } = useAuth(); + const colorScheme = useColorScheme(); + + if (loading) { + return ( + + + + ); + } + + if (!token) { + return ; + } + + return ( + + , + }} + /> + , + }} + /> + , + }} + /> + + ); +} diff --git a/KonditionExpo/app/(tabs)/explore.tsx b/KonditionExpo/app/(tabs)/explore.tsx new file mode 100644 index 0000000000..1e3f34ba8d --- /dev/null +++ b/KonditionExpo/app/(tabs)/explore.tsx @@ -0,0 +1,147 @@ +import { StyleSheet, ScrollView, View } from 'react-native'; +import { Ionicons } from '@expo/vector-icons'; + +import { ThemedText } from '@/components/ThemedText'; +import { ThemedView } from '@/components/ThemedView'; +import { useThemeColor } from '@/hooks/useThemeColor'; + +interface FeatureItemProps { + number: number; + title: string; + icon: keyof typeof Ionicons.glyphMap; +} + +const FeatureItem = ({ number, title, icon }: FeatureItemProps) => { + const tintColor = useThemeColor({}, 'tint'); + + return ( + + + {number} + + + {title} + + + + ); +}; + +export default function ExploreScreen() { + const backgroundColor = useThemeColor({}, 'background'); + + return ( + + + + FEATURES + + + Explore all the features Kondition has to offer + + + + + + + + + + + + + + + + + + + + ); +} + +const styles = StyleSheet.create({ + container: { + flex: 1, + }, + headerContainer: { + padding: 20, + paddingTop: 60, + alignItems: 'center', + marginBottom: 20, + }, + headerTitle: { + fontSize: 32, + fontWeight: 'bold', + marginBottom: 10, + textAlign: 'center', + }, + headerSubtitle: { + fontSize: 16, + textAlign: 'center', + opacity: 0.8, + marginBottom: 10, + }, + featureItem: { + marginHorizontal: 20, + marginBottom: 16, + borderRadius: 12, + overflow: 'hidden', + }, + featureNumberContainer: { + paddingVertical: 8, + paddingHorizontal: 16, + alignItems: 'center', + justifyContent: 'center', + }, + featureNumber: { + color: 'white', + fontWeight: 'bold', + fontSize: 18, + }, + featureContent: { + padding: 16, + flexDirection: 'row', + justifyContent: 'space-between', + alignItems: 'center', + }, + featureTitle: { + fontSize: 18, + fontWeight: 'bold', + }, + featureIcon: { + marginLeft: 8, + }, +}); diff --git a/KonditionExpo/app/(tabs)/index.tsx b/KonditionExpo/app/(tabs)/index.tsx new file mode 100644 index 0000000000..d5bc3f4d0a --- /dev/null +++ b/KonditionExpo/app/(tabs)/index.tsx @@ -0,0 +1,2 @@ +import HomeScreen from '@/app/home'; +export default HomeScreen; \ No newline at end of file diff --git a/KonditionExpo/app/(tabs)/profile.tsx b/KonditionExpo/app/(tabs)/profile.tsx new file mode 100644 index 0000000000..45816086df --- /dev/null +++ b/KonditionExpo/app/(tabs)/profile.tsx @@ -0,0 +1,90 @@ +import React, { useState } from 'react'; +import { SafeAreaView, View, Text, StyleSheet, ScrollView, TouchableOpacity, Image, Switch } from 'react-native'; +import { useUser } from '@/contexts/UserContext'; +import { router } from 'expo-router'; + +const ProfileScreen = () => { + const { name, height, weight, age } = useUser(); + const [notificationsEnabled, setNotificationsEnabled] = useState(false); + + const toggleNotifications = () => setNotificationsEnabled(prev => !prev); + + return ( + + + {/* Profile Box */} + + {name || 'User'} + + Edit + + + + + Height + {height ? `${height} cm` : '-'} + + + Weight + {weight ? `${weight} kg` : '-'} + + + Age + {age > 0 ? age.toString() : '-'} + + + + + {/* Account Box */} + + Account + Personal Data + Achievement + Activity History + Workout Progress + + + {/* Notification Box */} + + Notification + + Pop-up Notifications + + + + + {/* Other Box */} + + Other + Privacy Policy + Contact Us + Settings + + + + ); +}; + +const styles = StyleSheet.create({ + container: { flex: 1, backgroundColor: '#FFFFFF' }, + content: { padding: 16, paddingBottom: 80 }, + profileBox: { backgroundColor: '#E5F1FF', borderRadius: 20, padding: 16, marginBottom: 24 }, + profileName: { fontSize: 24, fontWeight: 'bold', color: '#333' }, + editBtn: { backgroundColor: '#70A1FF', borderRadius: 12, paddingVertical: 6, paddingHorizontal: 12, alignSelf: 'flex-start', marginTop: 8 }, + editText: { color: '#FFF' }, + statsRow: { flexDirection: 'row', justifyContent: 'space-between', marginTop: 16 }, + statBox: { backgroundColor: '#FFF', borderRadius: 12, padding: 12, width: '30%', alignItems: 'center' }, + statLabel: { fontSize: 12, color: '#555' }, + statValue: { fontSize: 16, fontWeight: 'bold', color: '#333' }, + sectionBox: { backgroundColor: '#F5F8FF', borderRadius: 16, padding: 16, marginBottom: 24 }, + sectionTitle: { fontSize: 16, fontWeight: 'bold', color: '#333', marginBottom: 8 }, + optionItem: { fontSize: 14, color: '#555', paddingVertical: 6 }, + toggleRow: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center' }, +}); + +export default ProfileScreen; \ No newline at end of file diff --git a/KonditionExpo/app/(tabs)/progress.tsx b/KonditionExpo/app/(tabs)/progress.tsx new file mode 100644 index 0000000000..33e662d6ca --- /dev/null +++ b/KonditionExpo/app/(tabs)/progress.tsx @@ -0,0 +1,446 @@ +import React, { useState } from 'react'; +import { + SafeAreaView, + View, + Text, + StyleSheet, + ScrollView, + TouchableOpacity, + Modal, + Alert, + Dimensions +} from 'react-native'; +import { LineChart } from 'react-native-chart-kit'; +import { useWorkout, Workout } from '@/contexts/WorkoutContext'; +import { Button } from '@/components/ui/Button'; +import { Input } from '@/components/ui/Input'; +import { router } from 'expo-router'; + +const { width } = Dimensions.get('window'); + +interface WorkoutItemProps { + workout: Workout; + onPress: () => void; +} + +const WorkoutItem = ({ workout, onPress }: WorkoutItemProps) => { + const formatDate = (date: Date) => { + return date.toLocaleDateString('en-US', { + month: 'short', + day: 'numeric', + year: 'numeric' + }); + }; + + const getTotalSets = () => { + return workout.exercises.reduce((total, exercise) => total + exercise.sets.length, 0); + }; + + return ( + + + {workout.name} + {formatDate(workout.date)} + + + {workout.exercises.length} exercises + {getTotalSets()} sets + {workout.duration}min + + + ); +}; + +const ProgressScreen = () => { + const { workouts, currentWorkout, startWorkout } = useWorkout(); + const [showNewWorkoutModal, setShowNewWorkoutModal] = useState(false); + const [newWorkoutName, setNewWorkoutName] = useState(''); + + const backgroundColor = '#FFFFFF'; + const textColor = '#333'; + const tintColor = '#70A1FF'; + + const handleStartWorkout = () => { + if (!newWorkoutName.trim()) { + Alert.alert('Error', 'Please enter a workout name'); + return; + } + startWorkout(newWorkoutName.trim()); + setNewWorkoutName(''); + setShowNewWorkoutModal(false); + router.push('../workout'); + }; + + const getWeeklyWorkouts = () => { + const last7Days = Array.from({ length: 7 }, (_, i) => { + const date = new Date(); + date.setDate(date.getDate() - (6 - i)); + return date.toDateString(); + }); + + return last7Days.map(dateString => { + const workoutsOnDay = workouts.filter(w => + w.date.toDateString() === dateString + ); + return workoutsOnDay.length; + }); + }; + + const getWeeklyLabels = () => { + return Array.from({ length: 7 }, (_, i) => { + const date = new Date(); + date.setDate(date.getDate() - (6 - i)); + return date.toLocaleDateString('en-US', { weekday: 'short' }); + }); + }; + + const getTotalWorkouts = () => workouts.length; + const getThisWeekWorkouts = () => { + const startOfWeek = new Date(); + startOfWeek.setDate(startOfWeek.getDate() - startOfWeek.getDay()); + return workouts.filter(w => w.date >= startOfWeek).length; + }; + + const getAverageWorkoutDuration = () => { + if (workouts.length === 0) return 0; + const total = workouts.reduce((sum, w) => sum + w.duration, 0); + return Math.round(total / workouts.length); + }; + + const weeklyData = getWeeklyWorkouts(); + const weeklyLabels = getWeeklyLabels(); + + return ( + + + {/* Header */} + + Progress + + Track your fitness journey + + + + {/* Current Workout Banner */} + {currentWorkout && ( + + + Workout in Progress: {currentWorkout.name} + + router.push('../workout')} + > + Continue + + + )} + + {/* Stats Cards */} + + + {getTotalWorkouts()} + Total Workouts + + + {getThisWeekWorkouts()} + This Week + + + {getAverageWorkoutDuration()} + Avg Duration + + + + {/* Weekly Chart */} + {workouts.length > 0 && ( + + + Weekly Activity + + tintColor, + strokeWidth: 2, + }, + ], + }} + width={width - 64} + height={200} + chartConfig={{ + backgroundGradientFrom: backgroundColor, + backgroundGradientTo: backgroundColor, + color: () => tintColor, + strokeWidth: 2, + decimalPlaces: 0, + }} + bezier + style={styles.chart} + /> + + )} + + {/* Start New Workout Button */} + +