"use client"; import {Card, Title} from "@tremor/react"; import * as R from 'ramda'; import * as dFns from 'date-fns'; import CalendarHeatmap from 'react-calendar-heatmap'; import 'react-calendar-heatmap/dist/styles.css'; import '../../app/calendar-styles.css' import {Tooltip} from 'react-tooltip'; import {Data} from "@/data/fetchData"; const granularity = 4; function computeCompletionShade(value: number, dailyGoal: number) { const linearValue = Math.round((value / dailyGoal) * granularity); // If we did something, but not enough to reach the first level, return 1 if (linearValue == 0 && value > 0) return 1; // Clamp to the granularity if (linearValue > granularity) return granularity; return linearValue; } function useCalendarData(data: Data, initialDate: Date, endDate: Date) { const timeEntries = data.timeEntries; // Group by day, sum up seconds const grouped = R.groupBy((entry) => { return dFns.formatISO(dFns.startOfDay(dFns.parseISO(entry.start))); }, timeEntries); const summed = R.mapObjIndexed((entries) => { return R.sum((entries ?? []).map((entry) => entry.duration)) }, grouped); // Fill in missing days, hacky dFns.eachDayOfInterval({ start: initialDate, end: endDate, }).forEach((date) => { const key = dFns.formatISO(date); if (summed[key] == undefined) { summed[key] = 0; } }) return Object.entries(summed) .map(([key, value]) => ({ date: dFns.parseISO(key), count: value / (60 * 60), })) } export function CalendarOverviewCard({ data, goal, startTime, endTime, }: { data: Data, goal: number, startTime: string, endTime: string, }) { const initialDate = dFns.parseISO(startTime); const endDate = dFns.parseISO(endTime); const calendarData = useCalendarData(data, initialDate, endDate); return Overview `color-github-${computeCompletionShade(value?.count ?? 0, goal)}`} tooltipDataAttrs={(value: any) => { return value.date ? { 'data-tooltip-id': `calendar-tooltip`, 'data-tooltip-content': value.count ? `${dFns.format(value.date, 'EEE do')}: ${value.count.toFixed(2)} hours` : `${dFns.format(value.date, 'EEE do')}` } : undefined }} /> }