import { useState, useEffect } from 'react'; import axios from 'axios'; export const useProfileStats = (user) => { const [stats, setStats] = useState({ goalsMapped: 0, nodesCreated: 0, nodesCompleted: 0, dayStreak: 0, points: 0, xp: 0, maxXp: 1200, level: 1, achievements: [], loading: true }); const API_URL = import.meta.env.VITE_API_URL; useEffect(() => { if (!user) { setStats(prev => ({ ...prev, loading: false })); return; } const fetchStats = async () => { try { setStats(prev => ({ ...prev, loading: true })); console.log('[ProfileStats] fetchStats starting. API_URL =', API_URL, 'User =', user); // Fetch projects (goals) const graphsRes = await axios.get(`${API_URL}/graphs/user/${user.id}`); const graphs = graphsRes.data; console.log('[ProfileStats] Graphs fetched successfully:', graphs); const goalsMapped = graphs.length; // Fetch nodes for each project let nodesCreated = 0; let nodesCompleted = 0; let hasConditionalNodes = false; let hasFinalNodes = false; const nodesPromises = graphs.map(g => axios.get(`${API_URL}/graphs/${g.id}/full`).catch((err) => { console.warn(`[ProfileStats] Failed to fetch nodes for graph ${g.id}:`, err); return { data: { nodes: [] } }; })); const nodesResponses = await Promise.all(nodesPromises); nodesResponses.forEach(res => { const nodes = res.data?.nodes || []; nodesCreated += nodes.length; nodesCompleted += nodes.filter(n => n.state === 1 || n.State === 1 || n.state === 'Done' || n.State === 'Done').length; if (nodes.some(n => { const t = (n.type || n.Type || '').toLowerCase(); return t === 'conditional' || t === 'condition'; })) hasConditionalNodes = true; if (nodes.some(n => { const t = (n.type || n.Type || '').toLowerCase(); return t === 'final' || t === 'goal'; })) hasFinalNodes = true; }); console.log('[ProfileStats] Nodes count fetched:', nodesCreated, 'Completed:', nodesCompleted); // Fetch habits for streak let dayStreak = 0; try { const habitsRes = await axios.get(`${API_URL}/habits/user/${user.id}`); const habits = habitsRes.data; console.log('[ProfileStats] Habits fetched successfully:', habits); // Calculate overall day streak const uniqueDates = [...new Set(habits.map(h => new Date(h.checkedDate).toISOString().split('T')[0]))].sort().reverse(); let expectedDate = new Date(); for (const dateStr of uniqueDates) { const d = new Date(dateStr); if (d.toISOString().split('T')[0] === expectedDate.toISOString().split('T')[0]) { dayStreak++; expectedDate.setDate(expectedDate.getDate() - 1); } else if (d > expectedDate) { continue; } else { // If the most recent date was yesterday, that's fine, we might still have a streak if (dayStreak === 0 && d.toISOString().split('T')[0] === new Date(Date.now() - 86400000).toISOString().split('T')[0]) { dayStreak++; expectedDate = new Date(Date.now() - 86400000); expectedDate.setDate(expectedDate.getDate() - 1); } else { break; } } } } catch (e) { console.error('[ProfileStats] Failed to fetch habits:', e); } console.log('[ProfileStats] Day streak calculated:', dayStreak); // Calculate points, XP, level const points = (goalsMapped * 50) + (nodesCreated * 10) + (nodesCompleted * 20) + (dayStreak * 20); const level = Math.floor(points / 1200) + 1; const xp = points % 1200; console.log('[ProfileStats] Calculated totals: Points =', points, 'Level =', level, 'XP =', xp); // Fetch achievements from new API let achievements = []; try { console.log('[ProfileStats] Fetching achievements progress from API...'); const achRes = await axios.get(`${API_URL}/achievements/progress`); console.log('[ProfileStats] Achievements progress raw response:', achRes.data); const mapCategoryToColor = (category) => { switch (category) { case 'Projects': return 'bg-blue-100'; case 'Tasks': return 'bg-yellow-100'; case 'Habits': return 'bg-orange-100'; case 'System': return 'bg-gray-100'; default: return 'bg-green-100'; } }; const mapTitleToIcon = (title) => { if (title.includes('Canvas')) return 'Map'; if (title.includes('Connect') || title.includes('Decision')) return 'ShieldCheck'; if (title.includes('Zap')) return 'Zap'; if (title.includes('Daily')) return 'Flame'; if (title.includes('Clear Desk')) return 'Target'; return 'Trophy'; }; achievements = achRes.data.map(item => ({ icon: item.achievement.iconUrl || mapTitleToIcon(item.achievement.title), title: item.achievement.title, desc: item.achievement.description, unlocked: item.isUnlocked, color: mapCategoryToColor(item.achievement.category), currentValue: item.currentValue, targetValue: item.achievement.targetValue })); console.log('[ProfileStats] Achievements mapped successfully:', achievements); } catch (e) { console.error('[ProfileStats] Failed to fetch achievements:', e); // Fallback to empty if API fails achievements = []; } setStats({ goalsMapped, nodesCreated, nodesCompleted, dayStreak, points: points >= 1000 ? (points/1000).toFixed(1) + 'k' : points.toString(), xp, maxXp: 1200, level, achievements, loading: false }); } catch (err) { console.error('[ProfileStats] Critical error in fetchStats main loop:', err); setStats(prev => ({ ...prev, loading: false })); } }; fetchStats(); }, [user]); return stats; };