import React, { useState, useCallback, useEffect } from 'react'; import { useFetchGithubCommits } from '@/hooks/githubQueries/useFetchGithubCommits'; import { Tooltip } from 'primereact/tooltip'; import { formatDateTime } from "@/utils/time"; import useWindowWidth from '@/hooks/useWindowWidth'; const CombinedContributionChart = ({ session }) => { const [contributionData, setContributionData] = useState({}); const [totalContributions, setTotalContributions] = useState(0); const windowWidth = useWindowWidth(); const prepareProgressData = useCallback(() => { if (!session?.user?.userCourses) return {}; const activityData = {}; const allActivities = []; // Array to store all activities for logging // Process course activities session.user.userCourses.forEach(courseProgress => { if (courseProgress.started) { const startDate = new Date(courseProgress.startedAt); const date = new Date(startDate.getTime() - startDate.getTimezoneOffset() * 60000) .toLocaleDateString('en-CA'); activityData[date] = (activityData[date] || 0) + 1; allActivities.push({ type: 'course_started', name: courseProgress.course?.name, date: date }); } if (courseProgress.completed) { const completeDate = new Date(courseProgress.completedAt); const date = new Date(completeDate.getTime() - completeDate.getTimezoneOffset() * 60000) .toLocaleDateString('en-CA'); activityData[date] = (activityData[date] || 0) + 1; allActivities.push({ type: 'course_completed', name: courseProgress.course?.name, date: date }); } }); // Process lesson activities session.user.userLessons?.forEach(lessonProgress => { if (lessonProgress.opened) { const openDate = new Date(lessonProgress.openedAt); const date = new Date(openDate.getTime() - openDate.getTimezoneOffset() * 60000) .toLocaleDateString('en-CA'); activityData[date] = (activityData[date] || 0) + 1; allActivities.push({ type: 'lesson_started', name: lessonProgress.lesson?.name, date: date }); } if (lessonProgress.completed) { const completeDate = new Date(lessonProgress.completedAt); const date = new Date(completeDate.getTime() - completeDate.getTimezoneOffset() * 60000) .toLocaleDateString('en-CA'); activityData[date] = (activityData[date] || 0) + 1; allActivities.push({ type: 'lesson_completed', name: lessonProgress.lesson?.name, date: date }); } }); return activityData; }, [session]); const handleNewCommit = useCallback(({ contributionData, totalCommits }) => { const activityData = prepareProgressData(); // Create a new object with GitHub commits const combinedData = { ...contributionData }; // Add activities to the combined data Object.entries(activityData).forEach(([date, count]) => { combinedData[date] = (combinedData[date] || 0) + count; }); setContributionData(combinedData); setTotalContributions(totalCommits + Object.values(activityData).reduce((a, b) => a + b, 0)); }, [prepareProgressData]); const { data, isLoading, isFetching } = useFetchGithubCommits(session, handleNewCommit); // Initialize from cached data if available useEffect(() => { if (data && !isLoading) { const activityData = prepareProgressData(); const combinedData = { ...data.contributionData }; // Add activities to the combined data Object.entries(activityData).forEach(([date, count]) => { combinedData[date] = (combinedData[date] || 0) + count; }); setContributionData(combinedData); setTotalContributions(data.totalCommits + Object.values(activityData).reduce((a, b) => a + b, 0)); } }, [data, isLoading, prepareProgressData]); const getColor = useCallback((count) => { if (count === 0) return 'bg-gray-100'; if (count < 3) return 'bg-green-300'; if (count < 6) return 'bg-green-400'; if (count < 12) return 'bg-green-600'; return 'bg-green-700'; }, []); const generateCalendar = useCallback(() => { const today = new Date(); today.setHours(23, 59, 59, 999); // Calculate the start date (52 weeks + remaining days to today) const oneYearAgo = new Date(today); oneYearAgo.setDate(today.getDate() - 364); // 52 weeks * 7 days = 364 days // Start from the first Sunday before or on oneYearAgo const startDate = new Date(oneYearAgo); startDate.setDate(startDate.getDate() - startDate.getDay()); const calendar = []; // Create 7 rows for days of the week (Sunday to Saturday) for (let i = 0; i < 7; i++) { calendar[i] = []; } // Fill in the dates by week columns let currentDate = new Date(startDate); while (currentDate <= today) { const weekDay = currentDate.getDay(); const dateString = currentDate.toLocaleDateString('en-CA'); const githubCount = data?.contributionData[dateString] || 0; const activityCount = (contributionData[dateString] || 0) - (data?.contributionData[dateString] || 0); const totalCount = githubCount + activityCount; calendar[weekDay].push({ date: new Date(currentDate), count: totalCount, githubCount, activityCount }); currentDate.setDate(currentDate.getDate() + 1); } return calendar; }, [contributionData, data?.contributionData]); const calendar = generateCalendar(); const weekDays = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']; const getMonthLabels = useCallback(() => { const today = new Date(); const oneYearAgo = new Date(today); oneYearAgo.setFullYear(today.getFullYear() - 1); oneYearAgo.setDate(today.getDate() + 1); const months = []; let currentMonth = -1; for (let d = new Date(oneYearAgo); d <= today; d.setDate(d.getDate() + 1)) { const month = d.getMonth(); if (month !== currentMonth) { months.push({ name: d.toLocaleString('default', { month: 'short' }), index: calendar[0].findIndex( (_, weekIndex) => calendar[0][weekIndex]?.date.getMonth() === month ) }); currentMonth = month; } } return months; }, [calendar]); const getScaleClass = (width) => { if (width <= 800) return 'overflow-x-auto'; if (width <= 1000) return 'scale-95 origin-top-left'; return ''; }; return (