Skip to main content
Glama
Dinesh-Satram

Health & Fitness Coach MCP

useFitnessData.ts6.2 kB
import { useState, useEffect, useCallback } from 'react' import { fetchUserContext, logEntry, generatePlan, updateExerciseProgress, UserContext, LogEntryData, handleAPIError } from '@/lib/api' export function useFitnessData(userId: string = 'default-user') { const [context, setContext] = useState<UserContext | null>(null) const [loading, setLoading] = useState(true) const [error, setError] = useState<string | null>(null) const [isLogging, setIsLogging] = useState(false) const [isGeneratingPlan, setIsGeneratingPlan] = useState(false) // Fetch user context const refreshContext = useCallback(async () => { try { setLoading(true) console.log('🔄 Refreshing context for userId:', userId) const newContext = await fetchUserContext(userId) console.log('📊 New context received:', { workoutMinutes: newContext.todaysProgress?.workoutMinutes, workoutsCompleted: newContext.todaysProgress?.workoutsCompleted, workoutsCount: newContext.recentEntries?.workouts?.length }) // If we got valid data, clear any previous errors if (newContext && newContext.todaysProgress) { setError(null) setContext(newContext) console.log('✅ Context updated in state with valid data') } else { console.warn('⚠️ Received invalid context data:', newContext) // Set error for invalid data setError('Unable to load fitness data') } // Force a re-render by updating a timestamp console.log('🔄 Context refresh completed at:', new Date().toISOString()) } catch (err) { const errorMessage = handleAPIError(err) console.error('❌ Failed to fetch context:', err) console.error('❌ Error message:', errorMessage) // Set error on fetch failure setError(errorMessage) } finally { setLoading(false) } }, [userId]) // Removed 'context' dependency to prevent infinite loop // Log new entry const logNewEntry = useCallback(async ( entryType: 'workout' | 'nutrition' | 'feedback' | 'activity', entryData: LogEntryData ) => { try { setIsLogging(true) setError(null) await logEntry(userId, entryType, entryData) // Immediately refresh context to update UI await refreshContext() return { success: true } } catch (err) { const errorMessage = handleAPIError(err) setError(errorMessage) return { success: false, error: errorMessage } } finally { setIsLogging(false) } }, [userId, refreshContext]) // Generate fitness plan const generateNewPlan = useCallback(async () => { try { setIsGeneratingPlan(true) setError(null) const planResult = await generatePlan(userId) // Refresh context to get updated plan data await refreshContext() return { success: true, planText: planResult.planText, structuredPlan: planResult.structuredPlan, } } catch (err) { const errorMessage = handleAPIError(err) setError(errorMessage) return { success: false, error: errorMessage } } finally { setIsGeneratingPlan(false) } }, [userId, refreshContext]) // Update exercise completion status const markExerciseComplete = useCallback(async ( exerciseId: string, completed: boolean, notes?: string ) => { try { await updateExerciseProgress(userId, exerciseId, completed, notes) await refreshContext() return { success: true } } catch (err) { const errorMessage = handleAPIError(err) setError(errorMessage) return { success: false, error: errorMessage } } }, [userId, refreshContext]) // Clear error const clearError = useCallback(() => { setError(null) }, []) // Initialize data on mount useEffect(() => { refreshContext() }, [refreshContext]) // Listen for refresh events from chat useEffect(() => { const handleRefreshContext = () => { refreshContext() } window.addEventListener('refreshContext', handleRefreshContext) return () => { window.removeEventListener('refreshContext', handleRefreshContext) } }, [refreshContext]) // Auto-clear errors when we have valid data useEffect(() => { if (context && context.todaysProgress && error) { console.log('🧹 Auto-clearing error since we have valid context data') setError(null) } }, [context, error]) // Calculate dynamic progress data from context const progressData = context ? { workoutMinutes: context.todaysProgress.workoutMinutes, workoutProgress: Math.min((context.todaysProgress.workoutMinutes / context.currentTargets.workoutMinutesGoal) * 100, 100), caloriesConsumed: context.todaysProgress.caloriesConsumed, caloriesProgress: Math.min((context.todaysProgress.caloriesConsumed / context.currentTargets.calorieGoal) * 100, 100), stepsTaken: context.todaysProgress.stepsTaken, stepsProgress: Math.min((context.todaysProgress.stepsTaken / context.currentTargets.stepsGoal) * 100, 100), } : { workoutMinutes: 0, workoutProgress: 0, caloriesConsumed: 0, caloriesProgress: 0, stepsTaken: 0, stepsProgress: 0, } // Debug log when progressData changes useEffect(() => { console.log('📈 Progress data calculated:', progressData) if (context) { console.log('📈 Raw context todaysProgress:', context.todaysProgress) console.log('📈 Raw context currentPlan:', context.currentPlan ? 'Plan exists' : 'No plan') } }, [ progressData.workoutMinutes, progressData.caloriesConsumed, progressData.stepsTaken, progressData.workoutProgress, progressData.caloriesProgress, progressData.stepsProgress, context?.currentPlan ]) // Only depend on actual values, not the whole objects return { // Data context, progressData, // State loading, error, isLogging, isGeneratingPlan, // Actions refreshContext, logNewEntry, generateNewPlan, markExerciseComplete, clearError, } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Dinesh-Satram/fitness_coach_MCP'

If you have feedback or need assistance with the MCP directory API, please join our Discord server