Spaces:
Sleeping
Sleeping
| import type { Activity, ProcessedData } from '@/types'; | |
| /** | |
| * Format date to YYYY-MM-DD in local timezone (not UTC) | |
| */ | |
| function formatDateLocal(date: Date): string { | |
| const year = date.getFullYear(); | |
| const month = String(date.getMonth() + 1).padStart(2, '0'); | |
| const day = String(date.getDate()).padStart(2, '0'); | |
| return `${year}-${month}-${day}`; | |
| } | |
| export function processActivities(activities: Activity[], dateRange?: { start: Date; end: Date }): ProcessedData { | |
| if (activities.length === 0 && !dateRange) { | |
| return { | |
| activities: [], | |
| dates: [], | |
| distances: [], | |
| durations: [], | |
| tss: [], | |
| }; | |
| } | |
| // Sort activities by date | |
| const sortedActivities = [...activities].sort((a, b) => a.date.getTime() - b.date.getTime()); | |
| // Get date range | |
| const startDate = dateRange?.start || (sortedActivities.length > 0 ? new Date(sortedActivities[0].date) : new Date()); | |
| const endDate = dateRange?.end || (sortedActivities.length > 0 ? new Date(sortedActivities[sortedActivities.length - 1].date) : new Date()); | |
| // Create a map of date -> activities | |
| const activityMap = new Map<string, Activity[]>(); | |
| sortedActivities.forEach(activity => { | |
| const dateStr = formatDateLocal(activity.date); | |
| if (!activityMap.has(dateStr)) { | |
| activityMap.set(dateStr, []); | |
| } | |
| activityMap.get(dateStr)!.push(activity); | |
| }); | |
| // Generate all dates in range | |
| const dates: string[] = []; | |
| const distances: (number | null)[] = []; | |
| const durations: (number | null)[] = []; | |
| const tss: (number | null)[] = []; | |
| for (let d = new Date(startDate); d <= endDate; d.setDate(d.getDate() + 1)) { | |
| const dateStr = formatDateLocal(d); | |
| dates.push(dateStr); | |
| const dayActivities = activityMap.get(dateStr) || []; | |
| if (dayActivities.length === 0) { | |
| // No activity this day | |
| distances.push(null); | |
| durations.push(null); | |
| tss.push(null); | |
| } else { | |
| // Sum up activities for the day | |
| let totalDistance = 0; | |
| let totalDuration = 0; | |
| let totalTss = 0; | |
| let hasDistance = false; | |
| let hasDuration = false; | |
| let hasTss = false; | |
| dayActivities.forEach(activity => { | |
| if (activity.distance !== undefined) { | |
| totalDistance += activity.distance; | |
| hasDistance = true; | |
| } | |
| if (activity.duration !== undefined) { | |
| totalDuration += activity.duration; | |
| hasDuration = true; | |
| } | |
| if (activity.trainingStressScore !== undefined) { | |
| totalTss += activity.trainingStressScore; | |
| hasTss = true; | |
| } | |
| }); | |
| distances.push(hasDistance ? totalDistance : null); | |
| durations.push(hasDuration ? totalDuration : null); | |
| tss.push(hasTss ? totalTss : null); | |
| } | |
| } | |
| return { | |
| activities: sortedActivities, | |
| dates, | |
| distances, | |
| durations, | |
| tss, | |
| }; | |
| } | |