|
|
import React from "react"; |
|
|
import { Tooltip } from "@mui/material"; |
|
|
import { getWeekDateRange } from "../utils/weeklyCalendar"; |
|
|
|
|
|
type WeeklyActivity = { |
|
|
date: string; |
|
|
count: number; |
|
|
level: number; |
|
|
}; |
|
|
|
|
|
type WeeklyHeatmapProps = { |
|
|
data: WeeklyActivity[]; |
|
|
color: string; |
|
|
}; |
|
|
|
|
|
const WeeklyHeatmap: React.FC<WeeklyHeatmapProps> = ({ data, color }) => { |
|
|
|
|
|
const groupedData = data.reduce((acc, activity) => { |
|
|
const date = new Date(activity.date); |
|
|
const yearMonth = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}`; |
|
|
|
|
|
if (!acc[yearMonth]) { |
|
|
acc[yearMonth] = []; |
|
|
} |
|
|
acc[yearMonth].push(activity); |
|
|
return acc; |
|
|
}, {} as Record<string, WeeklyActivity[]>); |
|
|
|
|
|
|
|
|
const getColorIntensity = (level: number) => { |
|
|
if (level === 0) return '#161b22'; |
|
|
const intensities = ['#0e4429', '#006d32', '#26a641', '#39d353']; |
|
|
return intensities[Math.min(level - 1, 3)] || color; |
|
|
}; |
|
|
|
|
|
|
|
|
const getMonthName = (yearMonth: string) => { |
|
|
const [year, month] = yearMonth.split('-'); |
|
|
const date = new Date(parseInt(year), parseInt(month) - 1); |
|
|
return date.toLocaleDateString('en-US', { month: 'short', year: 'numeric' }); |
|
|
}; |
|
|
|
|
|
const sortedMonths = Object.keys(groupedData).sort(); |
|
|
|
|
|
return ( |
|
|
<div className="w-full"> |
|
|
<div className="flex flex-wrap gap-4 justify-center"> |
|
|
{sortedMonths.map((yearMonth) => { |
|
|
const monthData = groupedData[yearMonth]; |
|
|
return ( |
|
|
<div key={yearMonth} className="flex flex-col items-center"> |
|
|
<div className="text-xs mb-2 text-gray-600 dark:text-gray-400"> |
|
|
{getMonthName(yearMonth)} |
|
|
</div> |
|
|
<div className="flex gap-1"> |
|
|
{monthData.map((activity, index) => ( |
|
|
<Tooltip |
|
|
key={`${yearMonth}-${index}`} |
|
|
title={`${activity.count} new repos in week of ${getWeekDateRange(activity.date)}`} |
|
|
arrow |
|
|
> |
|
|
<div |
|
|
className="w-3 h-3 rounded-sm cursor-pointer transition-opacity hover:opacity-80" |
|
|
style={{ |
|
|
backgroundColor: getColorIntensity(activity.level), |
|
|
}} |
|
|
/> |
|
|
</Tooltip> |
|
|
))} |
|
|
</div> |
|
|
</div> |
|
|
); |
|
|
})} |
|
|
</div> |
|
|
|
|
|
{/* Legend */} |
|
|
<div className="flex items-center justify-center mt-4 text-xs text-gray-600 dark:text-gray-400"> |
|
|
<span className="mr-2">Less</span> |
|
|
<div className="flex gap-1"> |
|
|
{[0, 1, 2, 3, 4].map((level) => ( |
|
|
<div |
|
|
key={level} |
|
|
className="w-2.5 h-2.5 rounded-sm" |
|
|
style={{ |
|
|
backgroundColor: getColorIntensity(level), |
|
|
}} |
|
|
/> |
|
|
))} |
|
|
</div> |
|
|
<span className="ml-2">More</span> |
|
|
</div> |
|
|
</div> |
|
|
); |
|
|
}; |
|
|
|
|
|
export default WeeklyHeatmap; |