Update src/app/lib/storage/workoutStorage.ts

This commit is contained in:
2026-03-11 19:53:50 +00:00
parent b8135f1fc0
commit b515f24749

View File

@@ -1,309 +1,99 @@
/**
* Workout and metrics data persistence layer
* Handles saving and retrieving workout data from localStorage
*/
export interface WorkoutSet {
reps: number;
weight: number;
timestamp: number;
}
// This file contains workout storage utilities
export interface WorkoutSession {
id: string;
exerciseName: string;
date: string;
sets: WorkoutSet[];
duration: number; // in seconds
caloriesBurned: number;
notes?: string;
duration: number;
exercises: Exercise[];
totalCalories: number;
}
export interface CardioSession {
export interface Exercise {
id: string;
type: 'running' | 'walking' | 'cycling';
date: string;
distance: number; // in km
duration: number; // in seconds
caloriesBurned: number;
pace: number; // km/h
steps?: number;
route?: string;
name: string;
sets: number;
reps: number;
weight: number;
}
export interface NutritionLog {
id: string;
date: string;
meals: Meal[];
totalCalories: number;
}
export interface Meal {
id: string;
name: string;
calories: number;
protein: number;
carbs: number;
fats: number;
meals: string[];
macros: {
protein: number;
carbs: number;
fats: number;
};
}
export interface UserMetrics {
totalWorkouts: number;
totalCardioDistance: number;
totalCaloriesBurned: number;
currentStreak: number;
personalRecords: Record<string, number>;
lastUpdated: number;
}
export const workoutStorage = {
// Store workout session
saveWorkout: (session: WorkoutSession): void => {
const workouts = JSON.parse(localStorage.getItem('workouts') || '[]');
workouts.push(session);
localStorage.setItem('workouts', JSON.stringify(workouts));
},
const STORAGE_KEYS = {
WORKOUT_SESSIONS: 'fitflow_workout_sessions',
CARDIO_SESSIONS: 'fitflow_cardio_sessions',
NUTRITION_LOGS: 'fitflow_nutrition_logs',
USER_METRICS: 'fitflow_user_metrics',
};
// Get all workouts
getAllWorkouts: (): WorkoutSession[] => {
const workouts = localStorage.getItem('workouts');
return workouts ? JSON.parse(workouts) : [];
},
/**
* Save a workout session to localStorage
*/
export const saveWorkoutSession = (session: WorkoutSession): void => {
if (typeof window === 'undefined') return;
try {
const sessions = getWorkoutSessions();
sessions.push(session);
localStorage.setItem(STORAGE_KEYS.WORKOUT_SESSIONS, JSON.stringify(sessions));
updateUserMetrics();
} catch (error) {
console.error('Error saving workout session:', error);
}
};
/**
* Get all workout sessions from localStorage
*/
export const getWorkoutSessions = (): WorkoutSession[] => {
if (typeof window === 'undefined') return [];
try {
const data = localStorage.getItem(STORAGE_KEYS.WORKOUT_SESSIONS);
return data ? JSON.parse(data) : [];
} catch (error) {
console.error('Error retrieving workout sessions:', error);
return [];
}
};
/**
* Save a cardio session to localStorage
*/
export const saveCardioSession = (session: CardioSession): void => {
if (typeof window === 'undefined') return;
try {
const sessions = getCardioSessions();
sessions.push(session);
localStorage.setItem(STORAGE_KEYS.CARDIO_SESSIONS, JSON.stringify(sessions));
updateUserMetrics();
} catch (error) {
console.error('Error saving cardio session:', error);
}
};
/**
* Get all cardio sessions from localStorage
*/
export const getCardioSessions = (): CardioSession[] => {
if (typeof window === 'undefined') return [];
try {
const data = localStorage.getItem(STORAGE_KEYS.CARDIO_SESSIONS);
return data ? JSON.parse(data) : [];
} catch (error) {
console.error('Error retrieving cardio sessions:', error);
return [];
}
};
/**
* Save a nutrition log to localStorage
*/
export const saveNutritionLog = (log: NutritionLog): void => {
if (typeof window === 'undefined') return;
try {
const logs = getNutritionLogs();
logs.push(log);
localStorage.setItem(STORAGE_KEYS.NUTRITION_LOGS, JSON.stringify(logs));
updateUserMetrics();
} catch (error) {
console.error('Error saving nutrition log:', error);
}
};
/**
* Get all nutrition logs from localStorage
*/
export const getNutritionLogs = (): NutritionLog[] => {
if (typeof window === 'undefined') return [];
try {
const data = localStorage.getItem(STORAGE_KEYS.NUTRITION_LOGS);
return data ? JSON.parse(data) : [];
} catch (error) {
console.error('Error retrieving nutrition logs:', error);
return [];
}
};
/**
* Get user metrics from localStorage
*/
export const getUserMetrics = (): UserMetrics => {
if (typeof window === 'undefined') {
return {
totalWorkouts: 0,
totalCardioDistance: 0,
totalCaloriesBurned: 0,
currentStreak: 0,
personalRecords: {},
lastUpdated: Date.now(),
};
}
try {
const data = localStorage.getItem(STORAGE_KEYS.USER_METRICS);
return data
? JSON.parse(data)
: {
totalWorkouts: 0,
totalCardioDistance: 0,
totalCaloriesBurned: 0,
currentStreak: 0,
personalRecords: {},
lastUpdated: Date.now(),
};
} catch (error) {
console.error('Error retrieving user metrics:', error);
return {
totalWorkouts: 0,
totalCardioDistance: 0,
totalCaloriesBurned: 0,
currentStreak: 0,
personalRecords: {},
lastUpdated: Date.now(),
};
}
};
/**
* Update user metrics based on saved sessions
*/
export const updateUserMetrics = (): void => {
if (typeof window === 'undefined') return;
try {
const workoutSessions = getWorkoutSessions();
const cardioSessions = getCardioSessions();
const nutritionLogs = getNutritionLogs();
let totalWorkouts = workoutSessions.length;
let totalCardioDistance = cardioSessions.reduce((sum, session) => sum + session.distance, 0);
let totalCaloriesBurned = workoutSessions.reduce((sum, session) => sum + session.caloriesBurned, 0)
+ cardioSessions.reduce((sum, session) => sum + session.caloriesBurned, 0);
// Calculate streak (consecutive days with activity)
const currentStreak = calculateStreak([...workoutSessions, ...cardioSessions]);
// Extract personal records from workouts
const personalRecords: Record<string, number> = {};
workoutSessions.forEach((session) => {
const maxWeight = Math.max(...session.sets.map((s) => s.weight));
const key = `${session.exerciseName}_pr`;
if (!personalRecords[key] || maxWeight > personalRecords[key]) {
personalRecords[key] = maxWeight;
}
// Get workouts by date range
getWorkoutsByDateRange: (startDate: Date, endDate: Date): WorkoutSession[] => {
const allWorkouts = workoutStorage.getAllWorkouts();
return allWorkouts.filter(w => {
const workoutDate = new Date(w.date);
return workoutDate >= startDate && workoutDate <= endDate;
});
},
const metrics: UserMetrics = {
// Delete workout
deleteWorkout: (id: string): void => {
const workouts = JSON.parse(localStorage.getItem('workouts') || '[]');
const filtered = workouts.filter((w: WorkoutSession) => w.id !== id);
localStorage.setItem('workouts', JSON.stringify(filtered));
},
// Get statistics
getStatistics: (): { totalWorkouts: number; totalCardioDistance: number; totalCaloriesBurned: number } => {
const allWorkouts = workoutStorage.getAllWorkouts();
const totalWorkouts = allWorkouts.length;
const totalCardioDistance = allWorkouts.reduce((sum, w) => sum + (w.duration * 0.15), 0);
const totalCaloriesBurned = allWorkouts.reduce((sum, w) => sum + w.totalCalories, 0);
return {
totalWorkouts,
totalCardioDistance,
totalCaloriesBurned,
currentStreak,
personalRecords,
lastUpdated: Date.now(),
};
},
localStorage.setItem(STORAGE_KEYS.USER_METRICS, JSON.stringify(metrics));
} catch (error) {
console.error('Error updating user metrics:', error);
}
};
/**
* Calculate consecutive days with activity
*/
const calculateStreak = (sessions: Array<WorkoutSession | CardioSession>): number => {
if (sessions.length === 0) return 0;
const dates = sessions.map((session) => new Date(session.date).toDateString()).filter((date, index, self) => self.indexOf(date) === index).sort((a, b) => new Date(b).getTime() - new Date(a).getTime());
let streak = 1;
for (let i = 1; i < dates.length; i++) {
const currentDate = new Date(dates[i]);
const previousDate = new Date(dates[i - 1]);
const diffTime = Math.abs(previousDate.getTime() - currentDate.getTime());
const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));
if (diffDays === 1) {
streak++;
} else {
break;
}
}
return streak;
};
/**
* Clear all stored data
*/
export const clearAllData = (): void => {
if (typeof window === 'undefined') return;
try {
localStorage.removeItem(STORAGE_KEYS.WORKOUT_SESSIONS);
localStorage.removeItem(STORAGE_KEYS.CARDIO_SESSIONS);
localStorage.removeItem(STORAGE_KEYS.NUTRITION_LOGS);
localStorage.removeItem(STORAGE_KEYS.USER_METRICS);
} catch (error) {
console.error('Error clearing data:', error);
}
};
/**
* Export data as JSON for backup
*/
export const exportData = (): string => {
const data = {
workouts: getWorkoutSessions(),
cardio: getCardioSessions(),
nutrition: getNutritionLogs(),
metrics: getUserMetrics(),
exportDate: new Date().toISOString(),
};
return JSON.stringify(data, null, 2);
};
/**
* Import data from JSON backup
*/
export const importData = (jsonData: string): boolean => {
if (typeof window === 'undefined') return false;
try {
const data = JSON.parse(jsonData);
if (data.workouts) localStorage.setItem(STORAGE_KEYS.WORKOUT_SESSIONS, JSON.stringify(data.workouts));
if (data.cardio) localStorage.setItem(STORAGE_KEYS.CARDIO_SESSIONS, JSON.stringify(data.cardio));
if (data.nutrition) localStorage.setItem(STORAGE_KEYS.NUTRITION_LOGS, JSON.stringify(data.nutrition));
if (data.metrics) localStorage.setItem(STORAGE_KEYS.USER_METRICS, JSON.stringify(data.metrics));
return true;
} catch (error) {
console.error('Error importing data:', error);
return false;
}
// Save nutrition log
saveNutritionLog: (log: NutritionLog): void => {
const logs = JSON.parse(localStorage.getItem('nutritionLogs') || '[]');
logs.push(log);
localStorage.setItem('nutritionLogs', JSON.stringify(logs));
},
// Get nutrition logs
getNutritionLogs: (): NutritionLog[] => {
const logs = localStorage.getItem('nutritionLogs');
return logs ? JSON.parse(logs) : [];
},
// Clear all data
clearAll: (): void => {
localStorage.removeItem('workouts');
localStorage.removeItem('nutritionLogs');
},
};