import React, { useState, useCallback } from 'react'; import { Tooltip } from 'primereact/tooltip'; import { formatDateTime } from "@/utils/time"; import useWindowWidth from '@/hooks/useWindowWidth'; const ActivityContributionChart = ({ session }) => { const [contributionData, setContributionData] = useState({}); const [totalActivities, setTotalActivities] = useState(0); const windowWidth = useWindowWidth(); // Prepare activity data const prepareActivityData = useCallback(() => { if (!session?.user?.userCourses) return {}; const activityData = {}; const allActivities = []; // 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) .toISOString().split('T')[0]; 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) .toISOString().split('T')[0]; 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) .toISOString().split('T')[0]; 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) .toISOString().split('T')[0]; activityData[date] = (activityData[date] || 0) + 1; allActivities.push({ type: 'lesson_completed', name: lessonProgress.lesson?.name, date: date }); } }); setContributionData(activityData); setTotalActivities(Object.values(activityData).reduce((a, b) => a + b, 0)); return activityData; }, [session]); // Initialize data React.useEffect(() => { prepareActivityData(); }, [prepareActivityData]); 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); // Start from the first Sunday before or on oneYearAgo const startDate = new Date(oneYearAgo); startDate.setDate(startDate.getDate() - startDate.getDay()); const calendar = []; 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(); // Use local timezone date string instead of ISO string const dateString = currentDate.toLocaleDateString('en-CA'); // YYYY-MM-DD format const activityCount = contributionData[dateString] || 0; calendar[weekDay].push({ date: new Date(currentDate), count: activityCount }); currentDate.setDate(currentDate.getDate() + 1); } return calendar; }, [contributionData]); const getMonthLabels = useCallback(() => { const today = new Date(); today.setHours(23, 59, 59, 999); // Calculate exactly 52 weeks back const oneYearAgo = new Date(today); oneYearAgo.setDate(today.getDate() - 364); // Start from the first Sunday const startDate = new Date(oneYearAgo); startDate.setDate(startDate.getDate() - startDate.getDay()); const months = []; let currentMonth = -1; const calendar = generateCalendar(); let currentDate = new Date(startDate); while (currentDate <= today) { const month = currentDate.getMonth(); if (month !== currentMonth) { months.push({ name: currentDate.toLocaleString('default', { month: 'short' }), index: calendar[0].findIndex( (_, weekIndex) => calendar[0][weekIndex]?.date.getMonth() === month ) }); currentMonth = month; } currentDate.setDate(currentDate.getDate() + 1); } return months; }, [generateCalendar]); const calendar = generateCalendar(); const weekDays = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']; const getScaleClass = (width) => { if (width <= 800) return 'scale-75 origin-top-left'; if (width <= 1000) return 'scale-95 origin-top-left'; return ''; }; return (

Activity

{totalActivities} learning activities in the last year

{/* Days of week labels */}
{weekDays.map((day, index) => (
{index % 2 === 0 && day}
))}
{/* Calendar grid */}
{calendar[0].map((_, weekIndex) => (
{calendar.map((row, dayIndex) => ( row[weekIndex] && (
0 ? `${row[weekIndex].count} activit${row[weekIndex].count !== 1 ? 'ies' : 'y'}` : 'No activities' }`} >
) ))}
))}
{/* Month labels */}
{getMonthLabels().map((month, index) => (
{month.name}
))}
{/* Legend */}
Less
More
); }; export default ActivityContributionChart;