import React, { useState, useCallback } from "react"; import { View, Text, StyleSheet, ScrollView, RefreshControl, TouchableOpacity, Alert, } from "react-native"; import { Ionicons } from "@expo/vector-icons"; import { useUser } from "@clerk/clerk-expo"; import { useFocusEffect } from "expo-router"; import * as SecureStore from "expo-secure-store"; import { useTheme } from "../../contexts/ThemeContext"; import { MinimalCard } from "../../components/MinimalCard"; import { SectionHeader } from "../../components/SectionHeader"; import { MinimalButton } from "../../components/MinimalButton"; import { Badge } from "../../components/Badge"; import { ProgressBar } from "../../components/ProgressBar"; import { GoalProgressCard } from "../../components/GoalProgressCard"; import { GoalCreationModal } from "../../components/GoalCreationModal"; import { WeeklyProgressChart } from "../../components/WeeklyProgressChart"; import { GoalTypeBreakdownChart } from "../../components/GoalTypeBreakdownChart"; import { useMembership } from "../../hooks/useMembership"; import type { FitnessGoal, CreateGoalData } from "../../services/fitnessGoals"; import { useStatistics } from "../../contexts/StatisticsContext"; import { useFitnessGoals } from "../../contexts/FitnessGoalsContext"; import { useRecommendations } from "../../contexts/RecommendationsContext"; import log from "../../utils/logger"; export default function GoalsScreen() { const { user } = useUser(); const { colors, typography } = useTheme(); const { statistics, refetchStatistics, clearCache: clearStatsCache, } = useStatistics(); const { goals, loading, refetchGoals, createGoal, completeGoal, deleteGoal, clearCache: clearGoalsCache, } = useFitnessGoals(); const { recommendations, clearCache: clearRecommendationsCache, refetchRecommendations, generateSelfPlan, } = useRecommendations(); const { membershipType, features } = useMembership(); const [refreshing, setRefreshing] = useState(false); const [isModalVisible, setIsModalVisible] = useState(false); const [showAnalytics, setShowAnalytics] = useState(false); const [isGeneratingPlan, setIsGeneratingPlan] = useState(false); const [showFullPlan, setShowFullPlan] = useState(false); const loadData = useCallback(async () => { await refetchGoals(); await refetchRecommendations(); await refetchStatistics(); }, [refetchGoals, refetchRecommendations, refetchStatistics]); const clearClerkCache = async () => { Alert.alert( "Clear Clerk Cache", "This will clear all cached Clerk tokens. You will need to sign out and sign back in.", [ { text: "Cancel", style: "cancel" }, { text: "Clear Cache", style: "destructive", onPress: async () => { try { const keysToDelete = [ "__clerk_client_jwt", "__clerk_db_jwt", "__clerk_client_uat", "__clerk_session_id", "__clerk_refresh_token", "__clerk_session_jwt", ]; for (const key of keysToDelete) { try { await SecureStore.deleteItemAsync(key); } catch (e) { // Key might not exist } } clearStatsCache(); clearGoalsCache(); clearRecommendationsCache(); Alert.alert( "Success", "Cache cleared! Please sign out and sign back in.", ); } catch (error) { log.error("Failed to clear cache", error); Alert.alert("Error", "Failed to clear cache"); } }, }, ], ); }; useFocusEffect( useCallback(() => { loadData(); }, [loadData]), ); const onRefresh = async () => { setRefreshing(true); await loadData(); setRefreshing(false); }; const handleCreateGoal = async (newGoal: CreateGoalData) => { await createGoal(newGoal); setIsModalVisible(false); }; const handleCompleteGoal = async (goal: FitnessGoal) => { await completeGoal(goal.id); }; const handleDeleteGoal = async (goalId: string) => { await deleteGoal(goalId); }; const activeGoals = goals?.filter((g) => g.status === "active") || []; const completedGoals = goals?.filter((g) => g.status === "completed") || []; const avgProgress = activeGoals.length > 0 ? Math.round( activeGoals.reduce((sum, g) => sum + (g.progress || 0), 0) / activeGoals.length, ) : 0; const latestApprovedRecommendation = [...recommendations] .filter((rec) => rec.status === "approved") .sort( (a, b) => new Date(b.generatedAt).getTime() - new Date(a.generatedAt).getTime(), )[0]; const isGoalAiAligned = (goal: FitnessGoal) => { if (goal.notes?.startsWith("[AI_LINKED]")) return true; if (!latestApprovedRecommendation?.activityPlan) return false; const planText = latestApprovedRecommendation.activityPlan.toLowerCase(); const title = goal.title.toLowerCase(); const description = (goal.description || "").toLowerCase(); const content = `${title} ${description}`; if (goal.goalType === "strength_milestone") { return planText.includes("strength") || planText.includes("weight"); } if (goal.goalType === "endurance_target") { return ( planText.includes("cardio") || planText.includes("endurance") || planText.includes("run") ); } if (goal.goalType === "flexibility_goal") { return planText.includes("stretch") || planText.includes("mobility"); } if (goal.goalType === "habit_building") { return planText.includes("habit") || planText.includes("daily"); } return ( planText.includes(content.split(" ")[0] || "") || content .split(" ") .some((word) => word.length > 4 && planText.includes(word)) ); }; const handleGenerateAiPlan = async () => { if (!features.recommendationsPerMonth || membershipType === "basic") { Alert.alert( "Premium Feature", "AI plan generation is available on Premium and VIP memberships.", ); return; } try { setIsGeneratingPlan(true); await generateSelfPlan(); await Promise.all([refetchRecommendations(), refetchGoals()]); Alert.alert( "Plan Ready", "Your new activity plan is ready and active goals were added.", ); } catch (error) { const message = error instanceof Error ? error.message : "Failed to generate AI activity plan."; Alert.alert("Could not generate plan", message); } finally { setIsGeneratingPlan(false); } }; return ( } > {/* Header */} Goals {activeGoals.length === 0 ? "Ready to crush some goals?" : activeGoals.length === 1 ? "You're on a mission! Keep it up!" : `${activeGoals.length} goals in progress. Let's go!`} {/* Stats Summary */} {goals && goals.length > 0 && ( {activeGoals.length} ACTIVE {completedGoals.length} COMPLETED {avgProgress}% PROGRESS )} {/* Analytics Section */} {statistics && (statistics.weeklyTrend.length > 0 || statistics.goals.goalsByType.length > 0) && ( setShowAnalytics(!showAnalytics)} activeOpacity={0.85} > Progress Analytics {showAnalytics ? "Tap to collapse" : "Tap to expand"} {showAnalytics && ( {statistics.weeklyTrend.length > 0 && ( Weekly Trend )} {statistics.goals.goalsByType.length > 0 && ( Goals by Type )} )} )} {/* Active Goals */} AI Activity Plan {latestApprovedRecommendation ? `Generated ${new Date(latestApprovedRecommendation.generatedAt).toLocaleDateString()}` : "Generate a plan aligned to your fitness profile"} {latestApprovedRecommendation ? ( <> {latestApprovedRecommendation.activityPlan} setShowFullPlan((prev) => !prev)} style={styles.showPlanButton} > {showFullPlan ? "Show less" : "View full plan"} ) : ( No AI activity plan yet. Generate one to get personalized guidance. )} setIsModalVisible(true)} /> {activeGoals.length === 0 ? ( 🎯 No active goals yet Tap "Add New" to set your first goal! 💪 ) : ( {activeGoals.map((goal) => ( handleCompleteGoal(goal)} onDelete={() => handleDeleteGoal(goal.id)} /> ))} )} {/* Completed Goals */} {completedGoals.length > 0 && ( {completedGoals.map((goal) => ( handleDeleteGoal(goal.id)} /> ))} )} {/* Floating Action Button - Minimal Style */} setIsModalVisible(true)} activeOpacity={0.8} style={[ styles.fab, { backgroundColor: colors.primary, shadowColor: colors.primary, }, ]} > {/* Create Goal Modal */} setIsModalVisible(false)} onSubmit={handleCreateGoal} /> ); } const styles = StyleSheet.create({ container: { flex: 1, }, scrollContent: { paddingBottom: 20, }, header: { flexDirection: "row", justifyContent: "space-between", alignItems: "flex-start", paddingHorizontal: 20, paddingTop: 60, paddingBottom: 20, }, debugButton: { padding: 8, }, section: { paddingHorizontal: 20, marginBottom: 24, }, statsRow: { flexDirection: "row", gap: 12, }, statCard: { flex: 1, alignItems: "center", paddingVertical: 20, paddingHorizontal: 12, borderRadius: 20, }, analyticsCard: { padding: 20, }, analyticsHeader: { flexDirection: "row", justifyContent: "space-between", alignItems: "center", }, analyticsHeaderLeft: { flexDirection: "row", alignItems: "center", }, analyticsIcon: { width: 48, height: 48, borderRadius: 14, justifyContent: "center", alignItems: "center", marginRight: 14, }, analyticsToggle: { width: 36, height: 36, borderRadius: 10, justifyContent: "center", alignItems: "center", }, analyticsContent: { paddingTop: 24, marginTop: 20, borderTopWidth: 1, borderTopColor: "rgba(0,0,0,0.05)", }, aiPlanCard: { padding: 18, marginBottom: 16, }, aiPlanHeader: { flexDirection: "row", justifyContent: "space-between", alignItems: "center", }, aiPlanHeaderLeft: { flexDirection: "row", alignItems: "center", flex: 1, marginRight: 12, }, aiPlanIcon: { width: 40, height: 40, borderRadius: 12, justifyContent: "center", alignItems: "center", marginRight: 12, }, showPlanButton: { marginTop: 8, alignSelf: "flex-start", }, chartSection: { marginBottom: 20, }, goalsList: { gap: 16, }, emptyState: { alignItems: "center", paddingVertical: 40, }, footer: { height: 100, }, fabContainer: { position: "absolute", right: 20, bottom: 90, }, fab: { width: 64, height: 64, borderRadius: 22, justifyContent: "center", alignItems: "center", shadowOffset: { width: 0, height: 6 }, shadowOpacity: 0.35, shadowRadius: 12, elevation: 8, }, });