diff --git a/apps/admin/data/fitai.db b/apps/admin/data/fitai.db
index 25a5e7b..d0afb89 100644
Binary files a/apps/admin/data/fitai.db and b/apps/admin/data/fitai.db differ
diff --git a/apps/mobile/src/app/(tabs)/attendance.tsx b/apps/mobile/src/app/(tabs)/attendance.tsx
index d09fa5b..ea2ea4f 100644
--- a/apps/mobile/src/app/(tabs)/attendance.tsx
+++ b/apps/mobile/src/app/(tabs)/attendance.tsx
@@ -104,7 +104,7 @@ export default function AttendanceScreen() {
{/* Header */}
- Attendance
+ 📍 Attendance
- Track your gym visits
+ {activeCheckIn
+ ? "You're crushing it today! 💪"
+ : history.length === 0
+ ? "Ready to start your fitness journey? 🚀"
+ : "Track your gym visits and build streaks! 🔥"}
{/* Check In/Out Section */}
{activeCheckIn ? (
-
+
- Currently Checked In
+ ✅ Currently Checked In
) : (
-
-
+
+
{/* Recent History */}
-
+
{history.length === 0 ? (
-
+
-
-
-
+ 📍
- Your check-in history will appear here
+ Check in to start building your streak! 🔥
@@ -232,7 +226,11 @@ export default function AttendanceScreen() {
: null;
return (
-
+
- Fitness Goals
+ 🎯 Fitness Goals
- Track your fitness journey progress
+ {activeGoals.length === 0
+ ? "Ready to crush some goals? 💪"
+ : activeGoals.length === 1
+ ? "You're on a mission! Keep it up! 🚀"
+ : `${activeGoals.length} goals in progress. Legend! ⭐`}
0 && (
-
+
{activeGoals.length}
@@ -189,11 +193,11 @@ export default function GoalsScreen() {
{ color: colors.textTertiary, marginTop: 4 },
]}
>
- Active
+ 🎯 Active
-
+
{completedGoals.length}
@@ -203,11 +207,11 @@ export default function GoalsScreen() {
{ color: colors.textTertiary, marginTop: 4 },
]}
>
- Completed
+ {completedGoals.length >= 5 ? "🏆" : "✅"} Completed
-
+
{avgProgress}%
@@ -217,7 +221,7 @@ export default function GoalsScreen() {
{ color: colors.textTertiary, marginTop: 4 },
]}
>
- Avg Progress
+ {avgProgress >= 75 ? "🔥" : "📊"} Progress
@@ -245,7 +249,7 @@ export default function GoalsScreen() {
{ color: colors.textPrimary, marginLeft: 8 },
]}
>
- Progress Analytics
+ 📈 Progress Analytics
setIsModalVisible(true)}
/>
{activeGoals.length === 0 ? (
-
+ 🎯
- Tap "Add New" to create your first goal
+ Tap "Add New" to set your first goal! 💪
@@ -331,7 +331,7 @@ export default function GoalsScreen() {
{completedGoals.length > 0 && (
{completedGoals.map((goal) => (
@@ -405,6 +405,7 @@ const styles = StyleSheet.create({
flex: 1,
alignItems: "center",
paddingVertical: 20,
+ borderRadius: 20,
},
analyticsHeader: {
flexDirection: "row",
diff --git a/apps/mobile/src/app/(tabs)/index.tsx b/apps/mobile/src/app/(tabs)/index.tsx
index 9dda6d7..8aa8a0b 100644
--- a/apps/mobile/src/app/(tabs)/index.tsx
+++ b/apps/mobile/src/app/(tabs)/index.tsx
@@ -5,9 +5,11 @@ import {
ScrollView,
RefreshControl,
Image,
+ Animated,
+ TouchableOpacity,
} from "react-native";
import { useUser } from "@clerk/clerk-expo";
-import { useState, useCallback, useEffect } from "react";
+import { useState, useCallback, useEffect, useRef } from "react";
import { useFocusEffect } from "@react-navigation/native";
import AsyncStorage from "@react-native-async-storage/async-storage";
import { Ionicons } from "@expo/vector-icons";
@@ -36,6 +38,11 @@ export default function HomeScreen() {
const [calories, setCalories] = useState(0);
const [waterIntake, setWaterIntake] = useState(0);
+ // Animation values
+ const streakPulse = useRef(new Animated.Value(1)).current;
+ const caloriesBounce = useRef(new Animated.Value(1)).current;
+ const waterBounce = useRef(new Animated.Value(1)).current;
+
// Refetch statistics when screen comes into focus
useFocusEffect(
useCallback(() => {
@@ -51,9 +58,46 @@ export default function HomeScreen() {
const getGreeting = () => {
const hour = new Date().getHours();
- if (hour < 12) return "Good Morning";
- if (hour < 18) return "Good Afternoon";
- return "Good Evening";
+ const greetings = {
+ morning: [
+ "Rise and Shine",
+ "Good Morning",
+ "Morning Champion",
+ "Ready to Crush It",
+ ],
+ afternoon: [
+ "Keep It Going",
+ "Good Afternoon",
+ "Stay Strong",
+ "You're Doing Great",
+ ],
+ evening: [
+ "Evening Warrior",
+ "Good Evening",
+ "Almost There",
+ "Finish Strong",
+ ],
+ };
+
+ const randomGreeting = (arr: string[]) =>
+ arr[Math.floor(Math.random() * arr.length)];
+
+ if (hour < 12) return randomGreeting(greetings.morning);
+ if (hour < 18) return randomGreeting(greetings.afternoon);
+ return randomGreeting(greetings.evening);
+ };
+
+ const getMotivationalEmoji = () => {
+ const emojis = ["💪", "🔥", "⚡", "🎯", "🚀", "✨"];
+ return emojis[Math.floor(Math.random() * emojis.length)];
+ };
+
+ const getStreakBadge = (streak: number) => {
+ if (streak >= 30) return "🏆";
+ if (streak >= 14) return "🔥";
+ if (streak >= 7) return "⭐";
+ if (streak >= 3) return "🌟";
+ return "💫";
};
const handleSaveMeal = (meal: {
@@ -63,11 +107,39 @@ export default function HomeScreen() {
}) => {
setCalories((prev) => prev + meal.calories);
setTrackMealModalVisible(false);
+
+ // Bounce animation
+ Animated.sequence([
+ Animated.timing(caloriesBounce, {
+ toValue: 1.2,
+ duration: 150,
+ useNativeDriver: true,
+ }),
+ Animated.timing(caloriesBounce, {
+ toValue: 1,
+ duration: 150,
+ useNativeDriver: true,
+ }),
+ ]).start();
};
const handleAddWater = (amount: number) => {
setWaterIntake((prev) => prev + amount);
setAddWaterModalVisible(false);
+
+ // Bounce animation
+ Animated.sequence([
+ Animated.timing(waterBounce, {
+ toValue: 1.2,
+ duration: 150,
+ useNativeDriver: true,
+ }),
+ Animated.timing(waterBounce, {
+ toValue: 1,
+ duration: 150,
+ useNativeDriver: true,
+ }),
+ ]).start();
};
const handleResetCalories = () => {
@@ -130,6 +202,9 @@ export default function HomeScreen() {
persistWater();
}, [waterIntake]);
+ const checkInsThisWeek = statistics?.attendance.checkInsThisWeek || 0;
+ const currentStreak = statistics?.attendance.currentStreak || 0;
+
// Check for midnight reset
useEffect(() => {
const checkAndResetIfNeeded = async () => {
@@ -167,8 +242,29 @@ export default function HomeScreen() {
return () => clearTimeout(midnightTimer);
}, []);
- const checkInsThisWeek = statistics?.attendance.checkInsThisWeek || 0;
- const currentStreak = statistics?.attendance.currentStreak || 0;
+ // Streak pulse animation
+ useEffect(() => {
+ const pulse = Animated.loop(
+ Animated.sequence([
+ Animated.timing(streakPulse, {
+ toValue: 1.1,
+ duration: 1000,
+ useNativeDriver: true,
+ }),
+ Animated.timing(streakPulse, {
+ toValue: 1,
+ duration: 1000,
+ useNativeDriver: true,
+ }),
+ ]),
+ );
+
+ if (currentStreak >= 3) {
+ pulse.start();
+ }
+
+ return () => pulse.stop();
+ }, [currentStreak]);
return (
@@ -184,20 +280,30 @@ export default function HomeScreen() {
>
{/* Header Section */}
-
-
- {getGreeting()},
-
+
+ {getGreeting()} {getMotivationalEmoji()}
+
+
{user?.firstName || "Athlete"}
+ {currentStreak >= 3 && (
+
+
+ {getStreakBadge(currentStreak)} {currentStreak} Day Streak!
+
+
+ )}
-
+
{user?.imageUrl ? (
) : (
@@ -210,193 +316,273 @@ export default function HomeScreen() {
)}
-
+
- {/* Daily Stats Card */}
+ {/* Daily Stats Card - More Playful */}
-
+
{checkInsThisWeek}
- This Week
+ Check-ins{"\n"}This Week
-
+
-
+
{calories}
- Kcal
+ Calories{"\n"}Tracked
-
+
-
+
-
+
+ {getStreakBadge(currentStreak)}
+
{currentStreak}
- Day Streak
+ Day{"\n"}Streak
-
+
- {/* Quick Actions */}
+ {/* Quick Actions - More Playful */}
-
+
- {
- console.log("Log workout tapped");
- }}
- style={styles.actionCard}
+ console.log("Log workout tapped")}
+ activeOpacity={0.7}
+ style={[
+ styles.actionCard,
+ {
+ backgroundColor: `${colors.primary}15`,
+ borderColor: `${colors.primary}30`,
+ borderWidth: 2,
+ },
+ ]}
>
-
+
- Log Workout
+ 💪 Workout
-
+
- setTrackMealModalVisible(true)}
- style={styles.actionCard}
+ activeOpacity={0.7}
+ style={[
+ styles.actionCard,
+ {
+ backgroundColor: `${colors.success}15`,
+ borderColor: `${colors.success}30`,
+ borderWidth: 2,
+ },
+ ]}
>
-
+
- Track Meal
+ 🍽️ Track Meal
-
+
- setAddWaterModalVisible(true)}
- style={styles.actionCard}
+ activeOpacity={0.7}
+ style={[
+ styles.actionCard,
+ {
+ backgroundColor: `${colors.info}15`,
+ borderColor: `${colors.info}30`,
+ borderWidth: 2,
+ },
+ ]}
>
-
-
-
+
+
+
+
+
- Add Water
+ 💧 Add Water
-
+
- setScanFoodModalVisible(true)}
- style={styles.actionCard}
+ activeOpacity={0.7}
+ style={[
+ styles.actionCard,
+ {
+ backgroundColor: `${colors.accent}15`,
+ borderColor: `${colors.accent}30`,
+ borderWidth: 2,
+ },
+ ]}
>
-
+
- Scan Food
+ 📱 Scan Food
-
+
- {/* Nutrition Progress */}
+ {/* Nutrition Progress - More Playful */}
-
-
+ = CALORIE_GOAL
+ ? "Goal smashed! 🎉"
+ : calories >= CALORIE_GOAL * 0.75
+ ? "Almost there! Keep going 💪"
+ : "Let's fuel that body! 🔥"
+ }
+ />
+
-
+ 🔥
{calories} / {CALORIE_GOAL}
- {/* Hydration Progress */}
+ {/* Hydration Progress - More Playful */}
-
+
-
-
- Hydration
-
+ 💧
+
+
+ Hydration
+
+
+ {waterIntake >= WATER_GOAL
+ ? "Perfectly hydrated! 🌊"
+ : "Stay hydrated, champ! 💪"}
+
+
{waterIntake} / {WATER_GOAL} ml
@@ -557,14 +762,33 @@ const styles = StyleSheet.create({
header: {
flexDirection: "row",
justifyContent: "space-between",
- alignItems: "center",
+ alignItems: "flex-start",
paddingHorizontal: 24,
marginBottom: 24,
},
+ greetingContainer: {
+ flex: 1,
+ marginRight: 16,
+ },
+ streakBadge: {
+ marginTop: 8,
+ paddingHorizontal: 12,
+ paddingVertical: 6,
+ backgroundColor: "rgba(136, 192, 208, 0.15)",
+ borderRadius: 999,
+ alignSelf: "flex-start",
+ },
+ streakBadgeText: {
+ fontSize: 13,
+ fontWeight: "600",
+ color: "#88C0D0",
+ },
avatar: {
width: 56,
height: 56,
borderRadius: 28,
+ borderWidth: 3,
+ borderColor: "#88C0D0",
},
placeholderAvatar: {
width: 56,
@@ -572,15 +796,21 @@ const styles = StyleSheet.create({
borderRadius: 28,
justifyContent: "center",
alignItems: "center",
+ borderWidth: 3,
+ borderColor: "#88C0D0",
},
section: {
paddingHorizontal: 24,
marginBottom: 24,
},
+ statsCard: {
+ borderRadius: 20,
+ },
statsRow: {
flexDirection: "row",
justifyContent: "space-between",
alignItems: "center",
+ paddingVertical: 8,
},
statItem: {
alignItems: "center",
@@ -588,7 +818,10 @@ const styles = StyleSheet.create({
},
divider: {
width: 1,
- height: 60,
+ height: 70,
+ },
+ emojiIcon: {
+ fontSize: 28,
},
actionGrid: {
flexDirection: "row",
@@ -598,7 +831,8 @@ const styles = StyleSheet.create({
actionCard: {
width: "48%",
alignItems: "center",
- paddingVertical: 20,
+ paddingVertical: 24,
+ borderRadius: 20,
},
progressHeader: {
flexDirection: "row",
diff --git a/apps/mobile/src/app/(tabs)/recommendations.tsx b/apps/mobile/src/app/(tabs)/recommendations.tsx
index 2594d2a..4ce68e2 100644
--- a/apps/mobile/src/app/(tabs)/recommendations.tsx
+++ b/apps/mobile/src/app/(tabs)/recommendations.tsx
@@ -132,7 +132,7 @@ export default function RecommendationsScreen() {
- AI Recommendations
+ ✨ AI Recommendations
- Personalized fitness & nutrition plans
+ {recommendations.length === 0
+ ? "Let's create your perfect plan! 🚀"
+ : `${recommendations.length} plan${recommendations.length !== 1 ? "s" : ""} ready for you! 💪`}
0
? `${recommendations.length} active plan${recommendations.length !== 1 ? "s" : ""}`
@@ -194,19 +196,9 @@ export default function RecommendationsScreen() {
/>
{recommendations.length === 0 ? (
-
+
-
-
-
+ 🤖
Tap "Generate New Plan" to get personalized AI-powered fitness
- and nutrition recommendations based on your profile and goals.
+ and nutrition recommendations! 🎯
@@ -258,7 +250,7 @@ function RecommendationCard({ recommendation }: RecommendationCardProps) {
const [expanded, setExpanded] = useState(false);
return (
-
+
{/* Header */}
setExpanded(!expanded)}
@@ -278,7 +270,7 @@ function RecommendationCard({ recommendation }: RecommendationCardProps) {
- AI Fitness Plan
+ ✨ AI Fitness Plan
{new Date(recommendation.generatedAt).toLocaleDateString()}
@@ -320,7 +312,7 @@ function RecommendationCard({ recommendation }: RecommendationCardProps) {
{ color: colors.textPrimary, marginLeft: 8 },
]}
>
- Activity Plan
+ 💪 Activity Plan
- Diet Plan
+ 🍽️ Diet Plan
{
+ if (isCompleted) {
+ Animated.sequence([
+ Animated.spring(scaleAnim, {
+ toValue: 1.05,
+ friction: 3,
+ tension: 40,
+ useNativeDriver: true,
+ }),
+ Animated.spring(scaleAnim, {
+ toValue: 1,
+ friction: 3,
+ tension: 40,
+ useNativeDriver: true,
+ }),
+ ]).start();
+ }
+ }, [isCompleted]);
+
+ const handleComplete = () => {
+ if (onComplete) {
+ // Trigger celebration animation
+ Animated.sequence([
+ Animated.spring(scaleAnim, {
+ toValue: 1.1,
+ friction: 3,
+ tension: 40,
+ useNativeDriver: true,
+ }),
+ Animated.spring(scaleAnim, {
+ toValue: 1,
+ friction: 3,
+ tension: 40,
+ useNativeDriver: true,
+ }),
+ ]).start(() => {
+ onComplete();
+ });
+ }
+ };
// Calculate days remaining
const daysRemaining = goal.targetDate
@@ -71,160 +121,169 @@ export function GoalProgressCard({
};
return (
-
-
- {/* Header */}
-
-
-
-
-
-
-
-
+
+
+ {/* Header */}
+
+
+
- {goal.title}
-
- {goal.description && (
+
+
+
+
- {goal.description}
+ {goal.title}
+ {goal.description && (
+
+ {goal.description}
+
+ )}
+
+
+
+ {/* Action Buttons */}
+
+ {!isCompleted && onComplete && (
+
+
+
+ )}
+ {onDelete && (
+
+
+
)}
- {/* Action Buttons */}
-
- {!isCompleted && onComplete && (
-
-
-
- )}
- {onDelete && (
-
-
-
- )}
-
-
+ {/* Progress Section */}
+ {goal.targetValue && (
+
+
+
+ {goal.currentValue || 0} / {goal.targetValue}{" "}
+ {goal.unit || ""}
+
+
+ {(progress * 100).toFixed(0)}%
+
+
- {/* Progress Section */}
- {goal.targetValue && (
-
-
-
- {goal.currentValue || 0} / {goal.targetValue} {goal.unit || ""}
-
+
+
+ )}
+
+ {/* Footer */}
+
+ {isCompleted ? (
+
+ ) : (
+
+ {goal.priority === "high" && (
+
+ )}
+ {goal.priority === "medium" && (
+
+ )}
+ {goal.priority === "low" && (
+
+ )}
+
+ )}
+
+ {daysRemaining !== null && !isCompleted && (
- {(progress * 100).toFixed(0)}%
+ {daysRemaining < 0
+ ? `${Math.abs(daysRemaining)} days overdue`
+ : `${daysRemaining} days remaining`}
-
+ )}
-
+ {isCompleted && goal.completedDate && (
+
+ Completed {new Date(goal.completedDate).toLocaleDateString()}
+
+ )}
- )}
-
- {/* Footer */}
-
- {isCompleted ? (
-
- ) : (
-
- {goal.priority === "high" && (
-
- )}
- {goal.priority === "medium" && (
-
- )}
- {goal.priority === "low" && (
-
- )}
-
- )}
-
- {daysRemaining !== null && !isCompleted && (
-
- {daysRemaining < 0
- ? `${Math.abs(daysRemaining)} days overdue`
- : `${daysRemaining} days remaining`}
-
- )}
-
- {isCompleted && goal.completedDate && (
-
- Completed {new Date(goal.completedDate).toLocaleDateString()}
-
- )}
-
-
-
+
+
+
);
}