redesign checkpoint
This commit is contained in:
parent
a5f761062e
commit
981208ab7b
Binary file not shown.
@ -104,7 +104,7 @@ export default function AttendanceScreen() {
|
|||||||
{/* Header */}
|
{/* Header */}
|
||||||
<View style={styles.header}>
|
<View style={styles.header}>
|
||||||
<Text style={[typography.h1, { color: colors.textPrimary }]}>
|
<Text style={[typography.h1, { color: colors.textPrimary }]}>
|
||||||
Attendance
|
📍 Attendance
|
||||||
</Text>
|
</Text>
|
||||||
<Text
|
<Text
|
||||||
style={[
|
style={[
|
||||||
@ -112,14 +112,18 @@ export default function AttendanceScreen() {
|
|||||||
{ color: colors.textSecondary, marginTop: 4 },
|
{ color: colors.textSecondary, marginTop: 4 },
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
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! 🔥"}
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
{/* Check In/Out Section */}
|
{/* Check In/Out Section */}
|
||||||
<View style={styles.section}>
|
<View style={styles.section}>
|
||||||
{activeCheckIn ? (
|
{activeCheckIn ? (
|
||||||
<MinimalCard variant="elevated" style={styles.activeCard}>
|
<MinimalCard variant="bordered" style={styles.activeCard}>
|
||||||
<View style={styles.activeHeader}>
|
<View style={styles.activeHeader}>
|
||||||
<View style={styles.activeHeaderLeft}>
|
<View style={styles.activeHeaderLeft}>
|
||||||
<IconContainer
|
<IconContainer
|
||||||
@ -135,7 +139,7 @@ export default function AttendanceScreen() {
|
|||||||
</IconContainer>
|
</IconContainer>
|
||||||
<View style={{ marginLeft: 12 }}>
|
<View style={{ marginLeft: 12 }}>
|
||||||
<Text style={[typography.h3, { color: colors.textPrimary }]}>
|
<Text style={[typography.h3, { color: colors.textPrimary }]}>
|
||||||
Currently Checked In
|
✅ Currently Checked In
|
||||||
</Text>
|
</Text>
|
||||||
<Text
|
<Text
|
||||||
style={[
|
style={[
|
||||||
@ -165,7 +169,7 @@ export default function AttendanceScreen() {
|
|||||||
</MinimalCard>
|
</MinimalCard>
|
||||||
) : (
|
) : (
|
||||||
<MinimalButton
|
<MinimalButton
|
||||||
title="Check In"
|
title="💪 Check In"
|
||||||
onPress={handleCheckIn}
|
onPress={handleCheckIn}
|
||||||
variant="primary"
|
variant="primary"
|
||||||
size="lg"
|
size="lg"
|
||||||
@ -175,29 +179,19 @@ export default function AttendanceScreen() {
|
|||||||
|
|
||||||
{/* Attendance Calendar */}
|
{/* Attendance Calendar */}
|
||||||
<View style={styles.section}>
|
<View style={styles.section}>
|
||||||
<SectionHeader title="Calendar" />
|
<SectionHeader title="📅 Calendar" />
|
||||||
<MinimalCard variant="default">
|
<MinimalCard variant="default" style={{ borderRadius: 20 }}>
|
||||||
<AttendanceCalendar attendanceRecords={history} />
|
<AttendanceCalendar attendanceRecords={history} />
|
||||||
</MinimalCard>
|
</MinimalCard>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
{/* Recent History */}
|
{/* Recent History */}
|
||||||
<View style={styles.section}>
|
<View style={styles.section}>
|
||||||
<SectionHeader title="Recent History" />
|
<SectionHeader title="📊 Recent History" />
|
||||||
{history.length === 0 ? (
|
{history.length === 0 ? (
|
||||||
<MinimalCard variant="default">
|
<MinimalCard variant="default" style={{ borderRadius: 20 }}>
|
||||||
<View style={styles.emptyState}>
|
<View style={styles.emptyState}>
|
||||||
<IconContainer
|
<Text style={{ fontSize: 64 }}>📍</Text>
|
||||||
variant="colored"
|
|
||||||
backgroundColor={`${colors.primary}15`}
|
|
||||||
size="lg"
|
|
||||||
>
|
|
||||||
<Ionicons
|
|
||||||
name="calendar-outline"
|
|
||||||
size={32}
|
|
||||||
color={colors.primary}
|
|
||||||
/>
|
|
||||||
</IconContainer>
|
|
||||||
<Text
|
<Text
|
||||||
style={[
|
style={[
|
||||||
typography.bodyEmphasis,
|
typography.bodyEmphasis,
|
||||||
@ -216,7 +210,7 @@ export default function AttendanceScreen() {
|
|||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
Your check-in history will appear here
|
Check in to start building your streak! 🔥
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
</MinimalCard>
|
</MinimalCard>
|
||||||
@ -232,7 +226,11 @@ export default function AttendanceScreen() {
|
|||||||
: null;
|
: null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MinimalCard key={index} variant="default">
|
<MinimalCard
|
||||||
|
key={index}
|
||||||
|
variant="bordered"
|
||||||
|
style={{ borderRadius: 16 }}
|
||||||
|
>
|
||||||
<View style={styles.historyItem}>
|
<View style={styles.historyItem}>
|
||||||
<View style={styles.historyLeft}>
|
<View style={styles.historyLeft}>
|
||||||
<IconContainer
|
<IconContainer
|
||||||
@ -315,6 +313,7 @@ const styles = StyleSheet.create({
|
|||||||
},
|
},
|
||||||
activeCard: {
|
activeCard: {
|
||||||
padding: 20,
|
padding: 20,
|
||||||
|
borderRadius: 20,
|
||||||
},
|
},
|
||||||
activeHeader: {
|
activeHeader: {
|
||||||
flexDirection: "row",
|
flexDirection: "row",
|
||||||
|
|||||||
@ -152,7 +152,7 @@ export default function GoalsScreen() {
|
|||||||
<View style={styles.header}>
|
<View style={styles.header}>
|
||||||
<View>
|
<View>
|
||||||
<Text style={[typography.h1, { color: colors.textPrimary }]}>
|
<Text style={[typography.h1, { color: colors.textPrimary }]}>
|
||||||
Fitness Goals
|
🎯 Fitness Goals
|
||||||
</Text>
|
</Text>
|
||||||
<Text
|
<Text
|
||||||
style={[
|
style={[
|
||||||
@ -160,7 +160,11 @@ export default function GoalsScreen() {
|
|||||||
{ color: colors.textSecondary, marginTop: 4 },
|
{ color: colors.textSecondary, marginTop: 4 },
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
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! ⭐`}
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
@ -179,7 +183,7 @@ export default function GoalsScreen() {
|
|||||||
{goals && goals.length > 0 && (
|
{goals && goals.length > 0 && (
|
||||||
<View style={styles.section}>
|
<View style={styles.section}>
|
||||||
<View style={styles.statsRow}>
|
<View style={styles.statsRow}>
|
||||||
<MinimalCard variant="elevated" style={styles.statCard}>
|
<MinimalCard variant="bordered" style={styles.statCard}>
|
||||||
<Text style={[typography.stat, { color: colors.primary }]}>
|
<Text style={[typography.stat, { color: colors.primary }]}>
|
||||||
{activeGoals.length}
|
{activeGoals.length}
|
||||||
</Text>
|
</Text>
|
||||||
@ -189,11 +193,11 @@ export default function GoalsScreen() {
|
|||||||
{ color: colors.textTertiary, marginTop: 4 },
|
{ color: colors.textTertiary, marginTop: 4 },
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
Active
|
🎯 Active
|
||||||
</Text>
|
</Text>
|
||||||
</MinimalCard>
|
</MinimalCard>
|
||||||
|
|
||||||
<MinimalCard variant="elevated" style={styles.statCard}>
|
<MinimalCard variant="bordered" style={styles.statCard}>
|
||||||
<Text style={[typography.stat, { color: colors.success }]}>
|
<Text style={[typography.stat, { color: colors.success }]}>
|
||||||
{completedGoals.length}
|
{completedGoals.length}
|
||||||
</Text>
|
</Text>
|
||||||
@ -203,11 +207,11 @@ export default function GoalsScreen() {
|
|||||||
{ color: colors.textTertiary, marginTop: 4 },
|
{ color: colors.textTertiary, marginTop: 4 },
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
Completed
|
{completedGoals.length >= 5 ? "🏆" : "✅"} Completed
|
||||||
</Text>
|
</Text>
|
||||||
</MinimalCard>
|
</MinimalCard>
|
||||||
|
|
||||||
<MinimalCard variant="elevated" style={styles.statCard}>
|
<MinimalCard variant="bordered" style={styles.statCard}>
|
||||||
<Text style={[typography.stat, { color: colors.textPrimary }]}>
|
<Text style={[typography.stat, { color: colors.textPrimary }]}>
|
||||||
{avgProgress}%
|
{avgProgress}%
|
||||||
</Text>
|
</Text>
|
||||||
@ -217,7 +221,7 @@ export default function GoalsScreen() {
|
|||||||
{ color: colors.textTertiary, marginTop: 4 },
|
{ color: colors.textTertiary, marginTop: 4 },
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
Avg Progress
|
{avgProgress >= 75 ? "🔥" : "📊"} Progress
|
||||||
</Text>
|
</Text>
|
||||||
</MinimalCard>
|
</MinimalCard>
|
||||||
</View>
|
</View>
|
||||||
@ -245,7 +249,7 @@ export default function GoalsScreen() {
|
|||||||
{ color: colors.textPrimary, marginLeft: 8 },
|
{ color: colors.textPrimary, marginLeft: 8 },
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
Progress Analytics
|
📈 Progress Analytics
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
<Ionicons
|
<Ionicons
|
||||||
@ -283,18 +287,14 @@ export default function GoalsScreen() {
|
|||||||
{/* Active Goals */}
|
{/* Active Goals */}
|
||||||
<View style={styles.section}>
|
<View style={styles.section}>
|
||||||
<SectionHeader
|
<SectionHeader
|
||||||
title={`Active Goals (${activeGoals.length})`}
|
title={`🚀 Active Goals (${activeGoals.length})`}
|
||||||
actionLabel="Add New"
|
actionLabel="Add New"
|
||||||
onActionPress={() => setIsModalVisible(true)}
|
onActionPress={() => setIsModalVisible(true)}
|
||||||
/>
|
/>
|
||||||
{activeGoals.length === 0 ? (
|
{activeGoals.length === 0 ? (
|
||||||
<MinimalCard variant="default">
|
<MinimalCard variant="default">
|
||||||
<View style={styles.emptyState}>
|
<View style={styles.emptyState}>
|
||||||
<Ionicons
|
<Text style={{ fontSize: 64 }}>🎯</Text>
|
||||||
name="flag-outline"
|
|
||||||
size={48}
|
|
||||||
color={colors.borderLight}
|
|
||||||
/>
|
|
||||||
<Text
|
<Text
|
||||||
style={[
|
style={[
|
||||||
typography.bodyEmphasis,
|
typography.bodyEmphasis,
|
||||||
@ -309,7 +309,7 @@ export default function GoalsScreen() {
|
|||||||
{ color: colors.textTertiary, marginTop: 4 },
|
{ color: colors.textTertiary, marginTop: 4 },
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
Tap "Add New" to create your first goal
|
Tap "Add New" to set your first goal! 💪
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
</MinimalCard>
|
</MinimalCard>
|
||||||
@ -331,7 +331,7 @@ export default function GoalsScreen() {
|
|||||||
{completedGoals.length > 0 && (
|
{completedGoals.length > 0 && (
|
||||||
<View style={styles.section}>
|
<View style={styles.section}>
|
||||||
<SectionHeader
|
<SectionHeader
|
||||||
title={`Completed Goals (${completedGoals.length})`}
|
title={`✨ Completed Goals (${completedGoals.length})`}
|
||||||
/>
|
/>
|
||||||
<View style={styles.goalsList}>
|
<View style={styles.goalsList}>
|
||||||
{completedGoals.map((goal) => (
|
{completedGoals.map((goal) => (
|
||||||
@ -405,6 +405,7 @@ const styles = StyleSheet.create({
|
|||||||
flex: 1,
|
flex: 1,
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
paddingVertical: 20,
|
paddingVertical: 20,
|
||||||
|
borderRadius: 20,
|
||||||
},
|
},
|
||||||
analyticsHeader: {
|
analyticsHeader: {
|
||||||
flexDirection: "row",
|
flexDirection: "row",
|
||||||
|
|||||||
@ -5,9 +5,11 @@ import {
|
|||||||
ScrollView,
|
ScrollView,
|
||||||
RefreshControl,
|
RefreshControl,
|
||||||
Image,
|
Image,
|
||||||
|
Animated,
|
||||||
|
TouchableOpacity,
|
||||||
} from "react-native";
|
} from "react-native";
|
||||||
import { useUser } from "@clerk/clerk-expo";
|
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 { useFocusEffect } from "@react-navigation/native";
|
||||||
import AsyncStorage from "@react-native-async-storage/async-storage";
|
import AsyncStorage from "@react-native-async-storage/async-storage";
|
||||||
import { Ionicons } from "@expo/vector-icons";
|
import { Ionicons } from "@expo/vector-icons";
|
||||||
@ -36,6 +38,11 @@ export default function HomeScreen() {
|
|||||||
const [calories, setCalories] = useState(0);
|
const [calories, setCalories] = useState(0);
|
||||||
const [waterIntake, setWaterIntake] = 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
|
// Refetch statistics when screen comes into focus
|
||||||
useFocusEffect(
|
useFocusEffect(
|
||||||
useCallback(() => {
|
useCallback(() => {
|
||||||
@ -51,9 +58,46 @@ export default function HomeScreen() {
|
|||||||
|
|
||||||
const getGreeting = () => {
|
const getGreeting = () => {
|
||||||
const hour = new Date().getHours();
|
const hour = new Date().getHours();
|
||||||
if (hour < 12) return "Good Morning";
|
const greetings = {
|
||||||
if (hour < 18) return "Good Afternoon";
|
morning: [
|
||||||
return "Good Evening";
|
"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: {
|
const handleSaveMeal = (meal: {
|
||||||
@ -63,11 +107,39 @@ export default function HomeScreen() {
|
|||||||
}) => {
|
}) => {
|
||||||
setCalories((prev) => prev + meal.calories);
|
setCalories((prev) => prev + meal.calories);
|
||||||
setTrackMealModalVisible(false);
|
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) => {
|
const handleAddWater = (amount: number) => {
|
||||||
setWaterIntake((prev) => prev + amount);
|
setWaterIntake((prev) => prev + amount);
|
||||||
setAddWaterModalVisible(false);
|
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 = () => {
|
const handleResetCalories = () => {
|
||||||
@ -130,6 +202,9 @@ export default function HomeScreen() {
|
|||||||
persistWater();
|
persistWater();
|
||||||
}, [waterIntake]);
|
}, [waterIntake]);
|
||||||
|
|
||||||
|
const checkInsThisWeek = statistics?.attendance.checkInsThisWeek || 0;
|
||||||
|
const currentStreak = statistics?.attendance.currentStreak || 0;
|
||||||
|
|
||||||
// Check for midnight reset
|
// Check for midnight reset
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const checkAndResetIfNeeded = async () => {
|
const checkAndResetIfNeeded = async () => {
|
||||||
@ -167,8 +242,29 @@ export default function HomeScreen() {
|
|||||||
return () => clearTimeout(midnightTimer);
|
return () => clearTimeout(midnightTimer);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const checkInsThisWeek = statistics?.attendance.checkInsThisWeek || 0;
|
// Streak pulse animation
|
||||||
const currentStreak = statistics?.attendance.currentStreak || 0;
|
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 (
|
return (
|
||||||
<View style={[styles.container, { backgroundColor: colors.background }]}>
|
<View style={[styles.container, { backgroundColor: colors.background }]}>
|
||||||
@ -184,20 +280,30 @@ export default function HomeScreen() {
|
|||||||
>
|
>
|
||||||
{/* Header Section */}
|
{/* Header Section */}
|
||||||
<View style={styles.header}>
|
<View style={styles.header}>
|
||||||
<View>
|
<View style={styles.greetingContainer}>
|
||||||
<Text style={[typography.body, { color: colors.textSecondary }]}>
|
|
||||||
{getGreeting()},
|
|
||||||
</Text>
|
|
||||||
<Text
|
<Text
|
||||||
style={[
|
style={[
|
||||||
typography.h1,
|
typography.body,
|
||||||
{ color: colors.textPrimary, marginTop: 4 },
|
{ color: colors.textSecondary, marginBottom: 4 },
|
||||||
]}
|
]}
|
||||||
|
>
|
||||||
|
{getGreeting()} {getMotivationalEmoji()}
|
||||||
|
</Text>
|
||||||
|
<Text
|
||||||
|
style={[typography.h1, { color: colors.textPrimary }]}
|
||||||
|
numberOfLines={1}
|
||||||
>
|
>
|
||||||
{user?.firstName || "Athlete"}
|
{user?.firstName || "Athlete"}
|
||||||
</Text>
|
</Text>
|
||||||
|
{currentStreak >= 3 && (
|
||||||
|
<View style={styles.streakBadge}>
|
||||||
|
<Text style={styles.streakBadgeText}>
|
||||||
|
{getStreakBadge(currentStreak)} {currentStreak} Day Streak!
|
||||||
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
<View>
|
)}
|
||||||
|
</View>
|
||||||
|
<TouchableOpacity activeOpacity={0.8}>
|
||||||
{user?.imageUrl ? (
|
{user?.imageUrl ? (
|
||||||
<Image source={{ uri: user.imageUrl }} style={styles.avatar} />
|
<Image source={{ uri: user.imageUrl }} style={styles.avatar} />
|
||||||
) : (
|
) : (
|
||||||
@ -210,193 +316,273 @@ export default function HomeScreen() {
|
|||||||
<Ionicons name="person" size={24} color={colors.white} />
|
<Ionicons name="person" size={24} color={colors.white} />
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
</View>
|
</TouchableOpacity>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
{/* Daily Stats Card */}
|
{/* Daily Stats Card - More Playful */}
|
||||||
<View style={styles.section}>
|
<View style={styles.section}>
|
||||||
<MinimalCard variant="elevated">
|
<MinimalCard variant="elevated" style={styles.statsCard}>
|
||||||
<View style={styles.statsRow}>
|
<View style={styles.statsRow}>
|
||||||
<View style={styles.statItem}>
|
<View style={styles.statItem}>
|
||||||
<IconContainer
|
<IconContainer
|
||||||
variant="colored"
|
variant="colored"
|
||||||
backgroundColor={`${colors.info}20`}
|
backgroundColor={colors.info}
|
||||||
|
size="lg"
|
||||||
>
|
>
|
||||||
<Ionicons
|
<Ionicons
|
||||||
name="checkmark-circle"
|
name="checkmark-circle"
|
||||||
size={24}
|
size={28}
|
||||||
color={colors.info}
|
color={colors.white}
|
||||||
/>
|
/>
|
||||||
</IconContainer>
|
</IconContainer>
|
||||||
<Text
|
<Text
|
||||||
style={[
|
style={[
|
||||||
typography.stat,
|
typography.stat,
|
||||||
{ color: colors.textPrimary, marginTop: 8 },
|
{ color: colors.textPrimary, marginTop: 12 },
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
{checkInsThisWeek}
|
{checkInsThisWeek}
|
||||||
</Text>
|
</Text>
|
||||||
<Text
|
<Text
|
||||||
style={[typography.caption, { color: colors.textTertiary }]}
|
style={[
|
||||||
|
typography.caption,
|
||||||
|
{ color: colors.textTertiary, textAlign: "center" },
|
||||||
|
]}
|
||||||
>
|
>
|
||||||
This Week
|
Check-ins{"\n"}This Week
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
<View
|
<View
|
||||||
style={[styles.divider, { backgroundColor: colors.border }]}
|
style={[
|
||||||
|
styles.divider,
|
||||||
|
{ backgroundColor: colors.borderLight },
|
||||||
|
]}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<View style={styles.statItem}>
|
<Animated.View
|
||||||
|
style={[
|
||||||
|
styles.statItem,
|
||||||
|
{ transform: [{ scale: caloriesBounce }] },
|
||||||
|
]}
|
||||||
|
>
|
||||||
<IconContainer
|
<IconContainer
|
||||||
variant="colored"
|
variant="colored"
|
||||||
backgroundColor={`${colors.danger}20`}
|
backgroundColor={colors.danger}
|
||||||
|
size="lg"
|
||||||
>
|
>
|
||||||
<Ionicons name="flame" size={24} color={colors.danger} />
|
<Ionicons name="flame" size={28} color={colors.white} />
|
||||||
</IconContainer>
|
</IconContainer>
|
||||||
<Text
|
<Text
|
||||||
style={[
|
style={[
|
||||||
typography.stat,
|
typography.stat,
|
||||||
{ color: colors.textPrimary, marginTop: 8 },
|
{ color: colors.textPrimary, marginTop: 12 },
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
{calories}
|
{calories}
|
||||||
</Text>
|
</Text>
|
||||||
<Text
|
<Text
|
||||||
style={[typography.caption, { color: colors.textTertiary }]}
|
style={[
|
||||||
|
typography.caption,
|
||||||
|
{ color: colors.textTertiary, textAlign: "center" },
|
||||||
|
]}
|
||||||
>
|
>
|
||||||
Kcal
|
Calories{"\n"}Tracked
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</Animated.View>
|
||||||
|
|
||||||
<View
|
<View
|
||||||
style={[styles.divider, { backgroundColor: colors.border }]}
|
style={[
|
||||||
|
styles.divider,
|
||||||
|
{ backgroundColor: colors.borderLight },
|
||||||
|
]}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<View style={styles.statItem}>
|
<Animated.View
|
||||||
|
style={[
|
||||||
|
styles.statItem,
|
||||||
|
{ transform: [{ scale: streakPulse }] },
|
||||||
|
]}
|
||||||
|
>
|
||||||
<IconContainer
|
<IconContainer
|
||||||
variant="colored"
|
variant="colored"
|
||||||
backgroundColor={`${colors.success}20`}
|
backgroundColor={colors.warning}
|
||||||
|
size="lg"
|
||||||
>
|
>
|
||||||
<Ionicons name="trophy" size={24} color={colors.success} />
|
<Text style={styles.emojiIcon}>
|
||||||
|
{getStreakBadge(currentStreak)}
|
||||||
|
</Text>
|
||||||
</IconContainer>
|
</IconContainer>
|
||||||
<Text
|
<Text
|
||||||
style={[
|
style={[
|
||||||
typography.stat,
|
typography.stat,
|
||||||
{ color: colors.textPrimary, marginTop: 8 },
|
{ color: colors.textPrimary, marginTop: 12 },
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
{currentStreak}
|
{currentStreak}
|
||||||
</Text>
|
</Text>
|
||||||
<Text
|
<Text
|
||||||
style={[typography.caption, { color: colors.textTertiary }]}
|
style={[
|
||||||
|
typography.caption,
|
||||||
|
{ color: colors.textTertiary, textAlign: "center" },
|
||||||
|
]}
|
||||||
>
|
>
|
||||||
Day Streak
|
Day{"\n"}Streak
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</Animated.View>
|
||||||
</View>
|
</View>
|
||||||
</MinimalCard>
|
</MinimalCard>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
{/* Quick Actions */}
|
{/* Quick Actions - More Playful */}
|
||||||
<View style={styles.section}>
|
<View style={styles.section}>
|
||||||
<SectionHeader title="Quick Actions" />
|
<SectionHeader
|
||||||
|
title="Quick Actions"
|
||||||
|
subtitle="What's your next move?"
|
||||||
|
/>
|
||||||
<View style={styles.actionGrid}>
|
<View style={styles.actionGrid}>
|
||||||
<MinimalCard
|
<TouchableOpacity
|
||||||
variant="default"
|
onPress={() => console.log("Log workout tapped")}
|
||||||
onPress={() => {
|
activeOpacity={0.7}
|
||||||
console.log("Log workout tapped");
|
style={[
|
||||||
}}
|
styles.actionCard,
|
||||||
style={styles.actionCard}
|
{
|
||||||
|
backgroundColor: `${colors.primary}15`,
|
||||||
|
borderColor: `${colors.primary}30`,
|
||||||
|
borderWidth: 2,
|
||||||
|
},
|
||||||
|
]}
|
||||||
>
|
>
|
||||||
<IconContainer
|
<IconContainer
|
||||||
variant="colored"
|
variant="colored"
|
||||||
backgroundColor={`${colors.primary}20`}
|
backgroundColor={colors.primary}
|
||||||
|
size="lg"
|
||||||
>
|
>
|
||||||
<Ionicons name="barbell" size={24} color={colors.primary} />
|
<Ionicons name="barbell" size={28} color={colors.white} />
|
||||||
</IconContainer>
|
</IconContainer>
|
||||||
<Text
|
<Text
|
||||||
style={[
|
style={[
|
||||||
typography.h3,
|
typography.h3,
|
||||||
{ color: colors.textPrimary, marginTop: 8 },
|
{ color: colors.textPrimary, marginTop: 12 },
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
Log Workout
|
💪 Workout
|
||||||
</Text>
|
</Text>
|
||||||
</MinimalCard>
|
</TouchableOpacity>
|
||||||
|
|
||||||
<MinimalCard
|
<TouchableOpacity
|
||||||
variant="default"
|
|
||||||
onPress={() => setTrackMealModalVisible(true)}
|
onPress={() => setTrackMealModalVisible(true)}
|
||||||
style={styles.actionCard}
|
activeOpacity={0.7}
|
||||||
|
style={[
|
||||||
|
styles.actionCard,
|
||||||
|
{
|
||||||
|
backgroundColor: `${colors.success}15`,
|
||||||
|
borderColor: `${colors.success}30`,
|
||||||
|
borderWidth: 2,
|
||||||
|
},
|
||||||
|
]}
|
||||||
>
|
>
|
||||||
<IconContainer
|
<IconContainer
|
||||||
variant="colored"
|
variant="colored"
|
||||||
backgroundColor={`${colors.success}20`}
|
backgroundColor={colors.success}
|
||||||
|
size="lg"
|
||||||
>
|
>
|
||||||
<Ionicons name="restaurant" size={24} color={colors.success} />
|
<Ionicons name="restaurant" size={28} color={colors.white} />
|
||||||
</IconContainer>
|
</IconContainer>
|
||||||
<Text
|
<Text
|
||||||
style={[
|
style={[
|
||||||
typography.h3,
|
typography.h3,
|
||||||
{ color: colors.textPrimary, marginTop: 8 },
|
{ color: colors.textPrimary, marginTop: 12 },
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
Track Meal
|
🍽️ Track Meal
|
||||||
</Text>
|
</Text>
|
||||||
</MinimalCard>
|
</TouchableOpacity>
|
||||||
|
|
||||||
<MinimalCard
|
<TouchableOpacity
|
||||||
variant="default"
|
|
||||||
onPress={() => setAddWaterModalVisible(true)}
|
onPress={() => setAddWaterModalVisible(true)}
|
||||||
style={styles.actionCard}
|
activeOpacity={0.7}
|
||||||
|
style={[
|
||||||
|
styles.actionCard,
|
||||||
|
{
|
||||||
|
backgroundColor: `${colors.info}15`,
|
||||||
|
borderColor: `${colors.info}30`,
|
||||||
|
borderWidth: 2,
|
||||||
|
},
|
||||||
|
]}
|
||||||
>
|
>
|
||||||
|
<Animated.View style={{ transform: [{ scale: waterBounce }] }}>
|
||||||
<IconContainer
|
<IconContainer
|
||||||
variant="colored"
|
variant="colored"
|
||||||
backgroundColor={`${colors.info}20`}
|
backgroundColor={colors.info}
|
||||||
|
size="lg"
|
||||||
>
|
>
|
||||||
<Ionicons name="water" size={24} color={colors.info} />
|
<Ionicons name="water" size={28} color={colors.white} />
|
||||||
</IconContainer>
|
</IconContainer>
|
||||||
|
</Animated.View>
|
||||||
<Text
|
<Text
|
||||||
style={[
|
style={[
|
||||||
typography.h3,
|
typography.h3,
|
||||||
{ color: colors.textPrimary, marginTop: 8 },
|
{ color: colors.textPrimary, marginTop: 12 },
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
Add Water
|
💧 Add Water
|
||||||
</Text>
|
</Text>
|
||||||
</MinimalCard>
|
</TouchableOpacity>
|
||||||
|
|
||||||
<MinimalCard
|
<TouchableOpacity
|
||||||
variant="default"
|
|
||||||
onPress={() => setScanFoodModalVisible(true)}
|
onPress={() => setScanFoodModalVisible(true)}
|
||||||
style={styles.actionCard}
|
activeOpacity={0.7}
|
||||||
|
style={[
|
||||||
|
styles.actionCard,
|
||||||
|
{
|
||||||
|
backgroundColor: `${colors.accent}15`,
|
||||||
|
borderColor: `${colors.accent}30`,
|
||||||
|
borderWidth: 2,
|
||||||
|
},
|
||||||
|
]}
|
||||||
>
|
>
|
||||||
<IconContainer
|
<IconContainer
|
||||||
variant="colored"
|
variant="colored"
|
||||||
backgroundColor={`${colors.accent}20`}
|
backgroundColor={colors.accent}
|
||||||
|
size="lg"
|
||||||
>
|
>
|
||||||
<Ionicons name="scan" size={24} color={colors.accent} />
|
<Ionicons name="scan" size={28} color={colors.white} />
|
||||||
</IconContainer>
|
</IconContainer>
|
||||||
<Text
|
<Text
|
||||||
style={[
|
style={[
|
||||||
typography.h3,
|
typography.h3,
|
||||||
{ color: colors.textPrimary, marginTop: 8 },
|
{ color: colors.textPrimary, marginTop: 12 },
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
Scan Food
|
📱 Scan Food
|
||||||
</Text>
|
</Text>
|
||||||
</MinimalCard>
|
</TouchableOpacity>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
{/* Nutrition Progress */}
|
{/* Nutrition Progress - More Playful */}
|
||||||
<View style={styles.section}>
|
<View style={styles.section}>
|
||||||
<SectionHeader title="Nutrition" />
|
<SectionHeader
|
||||||
<MinimalCard variant="default">
|
title="Today's Nutrition"
|
||||||
|
subtitle={
|
||||||
|
calories >= CALORIE_GOAL
|
||||||
|
? "Goal smashed! 🎉"
|
||||||
|
: calories >= CALORIE_GOAL * 0.75
|
||||||
|
? "Almost there! Keep going 💪"
|
||||||
|
: "Let's fuel that body! 🔥"
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<MinimalCard
|
||||||
|
variant="bordered"
|
||||||
|
style={{
|
||||||
|
backgroundColor: `${colors.danger}08`,
|
||||||
|
borderRadius: 12,
|
||||||
|
}}
|
||||||
|
>
|
||||||
<View style={styles.progressHeader}>
|
<View style={styles.progressHeader}>
|
||||||
<View style={styles.progressLabelRow}>
|
<View style={styles.progressLabelRow}>
|
||||||
<Ionicons name="flame" size={20} color={colors.textSecondary} />
|
<Text style={styles.emojiIcon}>🔥</Text>
|
||||||
<Text
|
<Text
|
||||||
style={[
|
style={[
|
||||||
typography.h3,
|
typography.h3,
|
||||||
@ -407,42 +593,61 @@ export default function HomeScreen() {
|
|||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
<Text
|
<Text
|
||||||
style={[typography.bodyEmphasis, { color: colors.textPrimary }]}
|
style={[
|
||||||
|
typography.bodyEmphasis,
|
||||||
|
{ color: colors.danger, fontWeight: "700" },
|
||||||
|
]}
|
||||||
>
|
>
|
||||||
{calories} / {CALORIE_GOAL}
|
{calories} / {CALORIE_GOAL}
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
<ProgressBar
|
<ProgressBar
|
||||||
progress={calories / CALORIE_GOAL}
|
progress={Math.min(calories / CALORIE_GOAL, 1)}
|
||||||
color={colors.danger}
|
color={colors.danger}
|
||||||
style={{ marginTop: 12 }}
|
style={{ marginTop: 12 }}
|
||||||
/>
|
/>
|
||||||
</MinimalCard>
|
</MinimalCard>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
{/* Hydration Progress */}
|
{/* Hydration Progress - More Playful */}
|
||||||
<View style={styles.section}>
|
<View style={styles.section}>
|
||||||
<MinimalCard variant="default">
|
<MinimalCard
|
||||||
|
variant="bordered"
|
||||||
|
style={{
|
||||||
|
backgroundColor: `${colors.info}08`,
|
||||||
|
borderRadius: 12,
|
||||||
|
}}
|
||||||
|
>
|
||||||
<View style={styles.progressHeader}>
|
<View style={styles.progressHeader}>
|
||||||
<View style={styles.progressLabelRow}>
|
<View style={styles.progressLabelRow}>
|
||||||
<Ionicons name="water" size={20} color={colors.textSecondary} />
|
<Text style={styles.emojiIcon}>💧</Text>
|
||||||
<Text
|
<View style={{ marginLeft: 8 }}>
|
||||||
style={[
|
<Text style={[typography.h3, { color: colors.textPrimary }]}>
|
||||||
typography.h3,
|
|
||||||
{ color: colors.textPrimary, marginLeft: 8 },
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
Hydration
|
Hydration
|
||||||
</Text>
|
</Text>
|
||||||
|
<Text
|
||||||
|
style={[
|
||||||
|
typography.caption,
|
||||||
|
{ color: colors.textTertiary, marginTop: 2 },
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
{waterIntake >= WATER_GOAL
|
||||||
|
? "Perfectly hydrated! 🌊"
|
||||||
|
: "Stay hydrated, champ! 💪"}
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
</View>
|
</View>
|
||||||
<Text
|
<Text
|
||||||
style={[typography.bodyEmphasis, { color: colors.textPrimary }]}
|
style={[
|
||||||
|
typography.bodyEmphasis,
|
||||||
|
{ color: colors.info, fontWeight: "700" },
|
||||||
|
]}
|
||||||
>
|
>
|
||||||
{waterIntake} / {WATER_GOAL} ml
|
{waterIntake} / {WATER_GOAL} ml
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
<ProgressBar
|
<ProgressBar
|
||||||
progress={waterIntake / WATER_GOAL}
|
progress={Math.min(waterIntake / WATER_GOAL, 1)}
|
||||||
color={colors.info}
|
color={colors.info}
|
||||||
style={{ marginTop: 12 }}
|
style={{ marginTop: 12 }}
|
||||||
/>
|
/>
|
||||||
@ -557,14 +762,33 @@ const styles = StyleSheet.create({
|
|||||||
header: {
|
header: {
|
||||||
flexDirection: "row",
|
flexDirection: "row",
|
||||||
justifyContent: "space-between",
|
justifyContent: "space-between",
|
||||||
alignItems: "center",
|
alignItems: "flex-start",
|
||||||
paddingHorizontal: 24,
|
paddingHorizontal: 24,
|
||||||
marginBottom: 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: {
|
avatar: {
|
||||||
width: 56,
|
width: 56,
|
||||||
height: 56,
|
height: 56,
|
||||||
borderRadius: 28,
|
borderRadius: 28,
|
||||||
|
borderWidth: 3,
|
||||||
|
borderColor: "#88C0D0",
|
||||||
},
|
},
|
||||||
placeholderAvatar: {
|
placeholderAvatar: {
|
||||||
width: 56,
|
width: 56,
|
||||||
@ -572,15 +796,21 @@ const styles = StyleSheet.create({
|
|||||||
borderRadius: 28,
|
borderRadius: 28,
|
||||||
justifyContent: "center",
|
justifyContent: "center",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
|
borderWidth: 3,
|
||||||
|
borderColor: "#88C0D0",
|
||||||
},
|
},
|
||||||
section: {
|
section: {
|
||||||
paddingHorizontal: 24,
|
paddingHorizontal: 24,
|
||||||
marginBottom: 24,
|
marginBottom: 24,
|
||||||
},
|
},
|
||||||
|
statsCard: {
|
||||||
|
borderRadius: 20,
|
||||||
|
},
|
||||||
statsRow: {
|
statsRow: {
|
||||||
flexDirection: "row",
|
flexDirection: "row",
|
||||||
justifyContent: "space-between",
|
justifyContent: "space-between",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
|
paddingVertical: 8,
|
||||||
},
|
},
|
||||||
statItem: {
|
statItem: {
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
@ -588,7 +818,10 @@ const styles = StyleSheet.create({
|
|||||||
},
|
},
|
||||||
divider: {
|
divider: {
|
||||||
width: 1,
|
width: 1,
|
||||||
height: 60,
|
height: 70,
|
||||||
|
},
|
||||||
|
emojiIcon: {
|
||||||
|
fontSize: 28,
|
||||||
},
|
},
|
||||||
actionGrid: {
|
actionGrid: {
|
||||||
flexDirection: "row",
|
flexDirection: "row",
|
||||||
@ -598,7 +831,8 @@ const styles = StyleSheet.create({
|
|||||||
actionCard: {
|
actionCard: {
|
||||||
width: "48%",
|
width: "48%",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
paddingVertical: 20,
|
paddingVertical: 24,
|
||||||
|
borderRadius: 20,
|
||||||
},
|
},
|
||||||
progressHeader: {
|
progressHeader: {
|
||||||
flexDirection: "row",
|
flexDirection: "row",
|
||||||
|
|||||||
@ -132,7 +132,7 @@ export default function RecommendationsScreen() {
|
|||||||
<View style={styles.header}>
|
<View style={styles.header}>
|
||||||
<View>
|
<View>
|
||||||
<Text style={[typography.h1, { color: colors.textPrimary }]}>
|
<Text style={[typography.h1, { color: colors.textPrimary }]}>
|
||||||
AI Recommendations
|
✨ AI Recommendations
|
||||||
</Text>
|
</Text>
|
||||||
<Text
|
<Text
|
||||||
style={[
|
style={[
|
||||||
@ -140,7 +140,9 @@ export default function RecommendationsScreen() {
|
|||||||
{ color: colors.textSecondary, marginTop: 4 },
|
{ color: colors.textSecondary, marginTop: 4 },
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
Personalized fitness & nutrition plans
|
{recommendations.length === 0
|
||||||
|
? "Let's create your perfect plan! 🚀"
|
||||||
|
: `${recommendations.length} plan${recommendations.length !== 1 ? "s" : ""} ready for you! 💪`}
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
@ -172,7 +174,7 @@ export default function RecommendationsScreen() {
|
|||||||
{/* Generate Button */}
|
{/* Generate Button */}
|
||||||
<View style={styles.section}>
|
<View style={styles.section}>
|
||||||
<MinimalButton
|
<MinimalButton
|
||||||
title="Generate New Plan"
|
title="🎯 Generate New Plan"
|
||||||
onPress={handleGenerateRecommendation}
|
onPress={handleGenerateRecommendation}
|
||||||
variant="primary"
|
variant="primary"
|
||||||
size="lg"
|
size="lg"
|
||||||
@ -185,7 +187,7 @@ export default function RecommendationsScreen() {
|
|||||||
{/* Recommendations List */}
|
{/* Recommendations List */}
|
||||||
<View style={styles.section}>
|
<View style={styles.section}>
|
||||||
<SectionHeader
|
<SectionHeader
|
||||||
title="Your Plans"
|
title="💡 Your Plans"
|
||||||
subtitle={
|
subtitle={
|
||||||
recommendations.length > 0
|
recommendations.length > 0
|
||||||
? `${recommendations.length} active plan${recommendations.length !== 1 ? "s" : ""}`
|
? `${recommendations.length} active plan${recommendations.length !== 1 ? "s" : ""}`
|
||||||
@ -194,19 +196,9 @@ export default function RecommendationsScreen() {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
{recommendations.length === 0 ? (
|
{recommendations.length === 0 ? (
|
||||||
<MinimalCard variant="default">
|
<MinimalCard variant="default" style={{ borderRadius: 20 }}>
|
||||||
<View style={styles.emptyState}>
|
<View style={styles.emptyState}>
|
||||||
<IconContainer
|
<Text style={{ fontSize: 64 }}>🤖</Text>
|
||||||
variant="colored"
|
|
||||||
backgroundColor={`${colors.accent}15`}
|
|
||||||
size="lg"
|
|
||||||
>
|
|
||||||
<Ionicons
|
|
||||||
name="sparkles-outline"
|
|
||||||
size={32}
|
|
||||||
color={colors.accent}
|
|
||||||
/>
|
|
||||||
</IconContainer>
|
|
||||||
<Text
|
<Text
|
||||||
style={[
|
style={[
|
||||||
typography.bodyEmphasis,
|
typography.bodyEmphasis,
|
||||||
@ -226,7 +218,7 @@ export default function RecommendationsScreen() {
|
|||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
Tap "Generate New Plan" to get personalized AI-powered fitness
|
Tap "Generate New Plan" to get personalized AI-powered fitness
|
||||||
and nutrition recommendations based on your profile and goals.
|
and nutrition recommendations! 🎯
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
</MinimalCard>
|
</MinimalCard>
|
||||||
@ -258,7 +250,7 @@ function RecommendationCard({ recommendation }: RecommendationCardProps) {
|
|||||||
const [expanded, setExpanded] = useState(false);
|
const [expanded, setExpanded] = useState(false);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MinimalCard variant="elevated">
|
<MinimalCard variant="bordered" style={{ borderRadius: 20 }}>
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
onPress={() => setExpanded(!expanded)}
|
onPress={() => setExpanded(!expanded)}
|
||||||
@ -278,7 +270,7 @@ function RecommendationCard({ recommendation }: RecommendationCardProps) {
|
|||||||
</IconContainer>
|
</IconContainer>
|
||||||
<View style={{ marginLeft: 12 }}>
|
<View style={{ marginLeft: 12 }}>
|
||||||
<Text style={[typography.h3, { color: colors.textPrimary }]}>
|
<Text style={[typography.h3, { color: colors.textPrimary }]}>
|
||||||
AI Fitness Plan
|
✨ AI Fitness Plan
|
||||||
</Text>
|
</Text>
|
||||||
<Text style={[typography.caption, { color: colors.textTertiary }]}>
|
<Text style={[typography.caption, { color: colors.textTertiary }]}>
|
||||||
{new Date(recommendation.generatedAt).toLocaleDateString()}
|
{new Date(recommendation.generatedAt).toLocaleDateString()}
|
||||||
@ -320,7 +312,7 @@ function RecommendationCard({ recommendation }: RecommendationCardProps) {
|
|||||||
{ color: colors.textPrimary, marginLeft: 8 },
|
{ color: colors.textPrimary, marginLeft: 8 },
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
Activity Plan
|
💪 Activity Plan
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
<Text
|
<Text
|
||||||
@ -348,7 +340,7 @@ function RecommendationCard({ recommendation }: RecommendationCardProps) {
|
|||||||
{ color: colors.textPrimary, marginLeft: 8 },
|
{ color: colors.textPrimary, marginLeft: 8 },
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
Diet Plan
|
🍽️ Diet Plan
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
<Text
|
<Text
|
||||||
@ -439,7 +431,7 @@ const styles = StyleSheet.create({
|
|||||||
},
|
},
|
||||||
planSection: {
|
planSection: {
|
||||||
padding: 16,
|
padding: 16,
|
||||||
borderRadius: 12,
|
borderRadius: 16,
|
||||||
},
|
},
|
||||||
planHeader: {
|
planHeader: {
|
||||||
flexDirection: "row",
|
flexDirection: "row",
|
||||||
|
|||||||
@ -1,5 +1,12 @@
|
|||||||
import React from "react";
|
import React, { useEffect, useRef } from "react";
|
||||||
import { View, Text, StyleSheet, TouchableOpacity, Alert } from "react-native";
|
import {
|
||||||
|
View,
|
||||||
|
Text,
|
||||||
|
StyleSheet,
|
||||||
|
TouchableOpacity,
|
||||||
|
Alert,
|
||||||
|
Animated,
|
||||||
|
} from "react-native";
|
||||||
import { Ionicons } from "@expo/vector-icons";
|
import { Ionicons } from "@expo/vector-icons";
|
||||||
import type { FitnessGoal } from "../services/fitnessGoals";
|
import type { FitnessGoal } from "../services/fitnessGoals";
|
||||||
import { useTheme } from "../contexts/ThemeContext";
|
import { useTheme } from "../contexts/ThemeContext";
|
||||||
@ -24,6 +31,49 @@ export function GoalProgressCard({
|
|||||||
const { colors, typography } = useTheme();
|
const { colors, typography } = useTheme();
|
||||||
const isCompleted = goal.status === "completed";
|
const isCompleted = goal.status === "completed";
|
||||||
const progress = (goal.progress || 0) / 100; // Convert to 0-1 scale
|
const progress = (goal.progress || 0) / 100; // Convert to 0-1 scale
|
||||||
|
const scaleAnim = useRef(new Animated.Value(1)).current;
|
||||||
|
|
||||||
|
// Celebration animation when goal is completed
|
||||||
|
useEffect(() => {
|
||||||
|
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
|
// Calculate days remaining
|
||||||
const daysRemaining = goal.targetDate
|
const daysRemaining = goal.targetDate
|
||||||
@ -71,6 +121,7 @@ export function GoalProgressCard({
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<Animated.View style={{ transform: [{ scale: scaleAnim }] }}>
|
||||||
<TouchableOpacity onPress={onPress} activeOpacity={0.7}>
|
<TouchableOpacity onPress={onPress} activeOpacity={0.7}>
|
||||||
<MinimalCard
|
<MinimalCard
|
||||||
variant="bordered"
|
variant="bordered"
|
||||||
@ -128,7 +179,7 @@ export function GoalProgressCard({
|
|||||||
<View style={styles.actions}>
|
<View style={styles.actions}>
|
||||||
{!isCompleted && onComplete && (
|
{!isCompleted && onComplete && (
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
onPress={onComplete}
|
onPress={handleComplete}
|
||||||
style={styles.actionButton}
|
style={styles.actionButton}
|
||||||
>
|
>
|
||||||
<Ionicons
|
<Ionicons
|
||||||
@ -160,7 +211,8 @@ export function GoalProgressCard({
|
|||||||
<Text
|
<Text
|
||||||
style={[typography.caption, { color: colors.textSecondary }]}
|
style={[typography.caption, { color: colors.textSecondary }]}
|
||||||
>
|
>
|
||||||
{goal.currentValue || 0} / {goal.targetValue} {goal.unit || ""}
|
{goal.currentValue || 0} / {goal.targetValue}{" "}
|
||||||
|
{goal.unit || ""}
|
||||||
</Text>
|
</Text>
|
||||||
<Text
|
<Text
|
||||||
style={[
|
style={[
|
||||||
@ -193,10 +245,16 @@ export function GoalProgressCard({
|
|||||||
<Badge variant="danger" label={goal.priority.toUpperCase()} />
|
<Badge variant="danger" label={goal.priority.toUpperCase()} />
|
||||||
)}
|
)}
|
||||||
{goal.priority === "medium" && (
|
{goal.priority === "medium" && (
|
||||||
<Badge variant="warning" label={goal.priority.toUpperCase()} />
|
<Badge
|
||||||
|
variant="warning"
|
||||||
|
label={goal.priority.toUpperCase()}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
{goal.priority === "low" && (
|
{goal.priority === "low" && (
|
||||||
<Badge variant="success" label={goal.priority.toUpperCase()} />
|
<Badge
|
||||||
|
variant="success"
|
||||||
|
label={goal.priority.toUpperCase()}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
@ -225,6 +283,7 @@ export function GoalProgressCard({
|
|||||||
</View>
|
</View>
|
||||||
</MinimalCard>
|
</MinimalCard>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
|
</Animated.View>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user