redesign completed

alot of work ahead
This commit is contained in:
dimitar committed 2025-11-26 02:31:46 +01:00
1 parent 73d2c4c1ed
commit febcdc111e
18 files changed
+1591 -845

No files matched your search

+55 -91
View File
@@ -1,117 +1,81 @@
import { useState, useEffect, useRef } from "react";
import {
View,
Text,
StyleSheet,
ScrollView,
TouchableOpacity,
ActivityIndicator,
RefreshControl,
Alert,
Animated,
} from "react-native";
import { useAuth } from "@clerk/clerk-expo";
import { Ionicons } from "@expo/vector-icons";
import { LinearGradient } from "expo-linear-gradient";
import { fitnessGoalsService, type FitnessGoal, type CreateGoalData } from "../../services/fitnessGoals";
import { GoalProgressCard } from "../../components/GoalProgressCard";
import { GoalCreationModal } from "../../components/GoalCreationModal";
import { theme } from "../../styles/theme";
import React, { useState, useCallback, useRef } from 'react';
import { View, Text, StyleSheet, ScrollView, RefreshControl, TouchableOpacity, Animated, Alert } from 'react-native';
import { Ionicons } from '@expo/vector-icons';
import { LinearGradient } from 'expo-linear-gradient';
import { theme } from '../../styles/theme';
import { GoalProgressCard } from '../../components/GoalProgressCard';
import { GoalCreationModal } from '../../components/GoalCreationModal';
import { useUser, useAuth } from "@clerk/clerk-expo";
import { fitnessGoalsService, type FitnessGoal, type CreateGoalData } from '../../services/fitnessGoals';
import { useFocusEffect } from 'expo-router';
export default function GoalsScreen() {
const { userId, getToken } = useAuth();
const { user } = useUser();
const { getToken } = useAuth();
const [goals, setGoals] = useState<FitnessGoal[]>([]);
const [loading, setLoading] = useState(true);
const [refreshing, setRefreshing] = useState(false);
const [showCreateModal, setShowCreateModal] = useState(false);
const [isModalVisible, setIsModalVisible] = useState(false);
const fabScale = useRef(new Animated.Value(1)).current;
const fetchGoals = async () => {
if (!userId) return;
const loadGoals = useCallback(async () => {
if (!user?.id) return;
try {
const token = await getToken();
const data = await fitnessGoalsService.getGoals(userId, token);
setGoals(data);
const loadedGoals = await fitnessGoalsService.getGoals(user.id, token);
setGoals(loadedGoals);
} catch (error) {
console.error("Error fetching fitness goals:", error);
Alert.alert("Error", "Failed to load goals. Please try again.");
} finally {
setLoading(false);
setRefreshing(false);
console.error('Error loading goals:', error);
}
};
}, [user?.id]); // Removed getToken from dependencies
useEffect(() => {
fetchGoals();
}, [userId]);
useFocusEffect(
useCallback(() => {
loadGoals();
}, [loadGoals])
);
const onRefresh = () => {
const onRefresh = async () => {
setRefreshing(true);
fetchGoals();
await loadGoals();
setRefreshing(false);
};
const handleCreateGoal = async (goalData: CreateGoalData) => {
try {
const token = await getToken();
const newGoal = await fitnessGoalsService.createGoal(goalData, token);
setGoals(prev => [newGoal, ...prev]);
Alert.alert("Success", "Goal created successfully!");
} catch (error) {
console.error("Error creating goal:", error);
throw error;
}
const handleCreateGoal = async (newGoal: CreateGoalData) => {
const token = await getToken();
await fitnessGoalsService.createGoal(newGoal, token);
await loadGoals();
setIsModalVisible(false);
};
const handleCompleteGoal = async (goalId: string) => {
try {
const token = await getToken();
const updatedGoal = await fitnessGoalsService.completeGoal(goalId, token);
setGoals(prev => prev.map(g => g.id === goalId ? updatedGoal : g));
Alert.alert("Success", "Goal completed! 🎉");
} catch (error) {
console.error("Error completing goal:", error);
Alert.alert("Error", "Failed to complete goal. Please try again.");
}
const handleCompleteGoal = async (goal: FitnessGoal) => {
const token = await getToken();
await fitnessGoalsService.completeGoal(goal.id, token);
await loadGoals();
};
const handleDeleteGoal = async (goalId: string) => {
try {
const token = await getToken();
await fitnessGoalsService.deleteGoal(goalId, token);
setGoals(prev => prev.filter(g => g.id !== goalId));
Alert.alert("Success", "Goal deleted");
} catch (error) {
console.error("Error deleting goal:", error);
Alert.alert("Error", "Failed to delete goal. Please try again.");
}
const token = await getToken();
await fitnessGoalsService.deleteGoal(goalId, token);
await loadGoals();
};
const activeGoals = goals.filter(g => g.status === 'active');
const completedGoals = goals.filter(g => g.status === 'completed');
if (loading && !refreshing) {
return (
<View style={styles.center}>
<ActivityIndicator size="large" color="#2563eb" />
</View>
);
}
return (
<View style={styles.container}>
<ScrollView
contentContainerStyle={styles.scrollContent}
refreshControl={
<RefreshControl refreshing={refreshing} onRefresh={onRefresh} />
<RefreshControl refreshing={refreshing} onRefresh={onRefresh} tintColor={theme.colors.primary} />
}
>
<LinearGradient
colors={theme.gradients.primary}
start={{ x: 0, y: 0 }}
end={{ x: 1, y: 1 }}
style={styles.header}
>
<View>
<Text style={styles.headerTitle}>My Fitness Goals</Text>
<Text style={styles.headerTitle}>Fitness Goals</Text>
<Text style={styles.headerSubtitle}>
Track your fitness journey progress
</Text>
@@ -133,7 +97,7 @@ export default function GoalsScreen() {
<Text style={styles.statValue}>
{activeGoals.length > 0
? Math.round(
activeGoals.reduce((sum, g) => sum + g.progress, 0) /
activeGoals.reduce((sum, g) => sum + (g.progress || 0), 0) /
activeGoals.length
)
: 0}%
@@ -161,7 +125,7 @@ export default function GoalsScreen() {
<GoalProgressCard
key={goal.id}
goal={goal}
onComplete={() => handleCompleteGoal(goal.id)}
onComplete={() => handleCompleteGoal(goal)}
onDelete={() => handleDeleteGoal(goal.id)}
/>
))
@@ -188,9 +152,9 @@ export default function GoalsScreen() {
</ScrollView>
{/* Floating Action Button */}
<Animated.View style={{ transform: [{ scale: fabScale }] }}>
<Animated.View style={[styles.fabContainer, { transform: [{ scale: fabScale }] }]}>
<TouchableOpacity
onPress={() => setShowCreateModal(true)}
onPress={() => setIsModalVisible(true)}
onPressIn={() => {
Animated.spring(fabScale, {
toValue: 0.9,
@@ -222,8 +186,8 @@ export default function GoalsScreen() {
{/* Create Goal Modal */}
<GoalCreationModal
visible={showCreateModal}
onClose={() => setShowCreateModal(false)}
visible={isModalVisible}
onClose={() => setIsModalVisible(false)}
onSubmit={handleCreateGoal}
/>
</View>
@@ -235,10 +199,8 @@ const styles = StyleSheet.create({
flex: 1,
backgroundColor: theme.colors.background,
},
center: {
flex: 1,
justifyContent: "center",
alignItems: "center",
scrollContent: {
paddingBottom: 20,
},
header: {
padding: 24,
@@ -268,7 +230,7 @@ const styles = StyleSheet.create({
backgroundColor: theme.colors.white,
padding: 16,
borderRadius: theme.borderRadius.xl,
alignItems: "center",
alignItems: 'center',
...theme.shadows.medium,
borderWidth: 1,
borderColor: "rgba(59, 130, 246, 0.1)",
@@ -312,10 +274,12 @@ const styles = StyleSheet.create({
footer: {
height: 100,
},
fab: {
fabContainer: {
position: "absolute",
right: 20,
bottom: 20,
bottom: 110, // Adjusted for tab bar height
},
fab: {
width: 64,
height: 64,
borderRadius: 32,