/** * Activity Feed Utilities * * Combines multiple data sources (check-ins, goals, nutrition) into a unified activity feed */ import type { AttendanceStatistics } from "../api/types"; import type { FitnessGoal } from "../services/fitnessGoals"; export enum ActivityType { GYM_CHECKIN = "gym_checkin", GOAL_COMPLETED = "goal_completed", GOAL_CREATED = "goal_created", NUTRITION_MILESTONE = "nutrition_milestone", } export interface ActivityItem { id: string; type: ActivityType; title: string; timestamp: Date; duration?: number; // in minutes metadata?: Record; } /** * Convert attendance check-ins to activity items */ export function checkInsToActivities( attendance: AttendanceStatistics, ): ActivityItem[] { return attendance.recentCheckIns.map((checkIn) => { const checkInDate = new Date(checkIn.checkInTime); return { id: `checkin-${checkIn.id}`, type: ActivityType.GYM_CHECKIN, title: "Gym Check-in", timestamp: checkInDate, duration: checkIn.duration || undefined, metadata: { checkOutTime: checkIn.checkOutTime, }, }; }); } /** * Convert completed goals to activity items */ export function completedGoalsToActivities( goals: FitnessGoal[], ): ActivityItem[] { return goals .filter((goal) => goal.status === "completed" && goal.completedDate) .map((goal) => { const completedDate = new Date(goal.completedDate!); return { id: `goal-completed-${goal.id}`, type: ActivityType.GOAL_COMPLETED, title: goal.title, timestamp: completedDate, metadata: { goalType: goal.goalType, priority: goal.priority, targetValue: goal.targetValue, unit: goal.unit, }, }; }); } /** * Convert recently created goals to activity items */ export function newGoalsToActivities( goals: FitnessGoal[], daysBack: number = 7, ): ActivityItem[] { const cutoffDate = new Date(); cutoffDate.setDate(cutoffDate.getDate() - daysBack); return goals .filter((goal) => { const createdDate = new Date(goal.createdAt); return goal.status === "active" && createdDate > cutoffDate; }) .map((goal) => { const createdDate = new Date(goal.createdAt); return { id: `goal-created-${goal.id}`, type: ActivityType.GOAL_CREATED, title: goal.title, timestamp: createdDate, metadata: { goalType: goal.goalType, priority: goal.priority, }, }; }); } /** * Combine and sort all activities by timestamp (most recent first) */ export function combineActivities( ...activityGroups: ActivityItem[][] ): ActivityItem[] { const allActivities = activityGroups.flat(); return allActivities.sort( (a, b) => b.timestamp.getTime() - a.timestamp.getTime(), ); } /** * Get recent activities limited to a specific count */ export function getRecentActivities( activities: ActivityItem[], limit: number = 5, ): ActivityItem[] { return activities.slice(0, limit); } /** * Format activity timestamp for display */ export function formatActivityTime(timestamp: Date): string { const now = new Date(); const diffMs = now.getTime() - timestamp.getTime(); const diffMins = Math.floor(diffMs / 60000); const diffHours = Math.floor(diffMs / 3600000); const diffDays = Math.floor(diffMs / 86400000); // Today if (diffDays === 0) { if (diffMins < 60) { return diffMins <= 1 ? "Just now" : `${diffMins}m ago`; } return `Today, ${timestamp.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit", })}`; } // Yesterday if (diffDays === 1) { return `Yesterday, ${timestamp.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit", })}`; } // This week (within 7 days) if (diffDays < 7) { return `${diffDays}d ago`; } // Older return timestamp.toLocaleDateString(); } /** * Format duration in minutes to human-readable string */ export function formatDuration(minutes?: number): string | undefined { if (!minutes) return undefined; if (minutes < 60) { return `${minutes}m`; } const hours = Math.floor(minutes / 60); const mins = minutes % 60; if (mins === 0) { return `${hours}h`; } return `${hours}h ${mins}m`; } /** * Get icon name for activity type */ export function getActivityIcon(type: ActivityType): string { switch (type) { case ActivityType.GYM_CHECKIN: return "barbell"; case ActivityType.GOAL_COMPLETED: return "trophy"; case ActivityType.GOAL_CREATED: return "flag"; case ActivityType.NUTRITION_MILESTONE: return "restaurant"; default: return "checkmark-circle"; } } /** * Get activity title prefix/emoji */ export function getActivityEmoji(type: ActivityType): string { switch (type) { case ActivityType.GYM_CHECKIN: return "💪"; case ActivityType.GOAL_COMPLETED: return "🏆"; case ActivityType.GOAL_CREATED: return "🎯"; case ActivityType.NUTRITION_MILESTONE: return "🍽️"; default: return "✅"; } }