Backend is here. Default admin is created if needed.
This commit is contained in:
@@ -1,137 +1,67 @@
|
||||
|
||||
import { WorkoutSession, ExerciseDef, ExerciseType, WorkoutSet, WorkoutPlan } from '../types';
|
||||
import { updateUserProfile } from './auth';
|
||||
import { api } from './api';
|
||||
|
||||
// Helper to namespace keys
|
||||
const getKey = (base: string, userId: string) => `${base}_${userId}`;
|
||||
|
||||
const SESSIONS_KEY = 'gymflow_sessions';
|
||||
const EXERCISES_KEY = 'gymflow_exercises'; // Custom exercises are per user
|
||||
const PLANS_KEY = 'gymflow_plans';
|
||||
|
||||
const DEFAULT_EXERCISES: ExerciseDef[] = [
|
||||
{ id: 'bp', name: 'Жим лежа', type: ExerciseType.STRENGTH },
|
||||
{ id: 'sq', name: 'Приседания со штангой', type: ExerciseType.STRENGTH },
|
||||
{ id: 'dl', name: 'Становая тяга', type: ExerciseType.STRENGTH },
|
||||
{ id: 'pu', name: 'Подтягивания', type: ExerciseType.BODYWEIGHT, bodyWeightPercentage: 100 },
|
||||
{ id: 'run', name: 'Бег', type: ExerciseType.CARDIO },
|
||||
{ id: 'plank', name: 'Планка', type: ExerciseType.STATIC, bodyWeightPercentage: 100 },
|
||||
{ id: 'dip', name: 'Отжимания на брусьях', type: ExerciseType.BODYWEIGHT, bodyWeightPercentage: 100 },
|
||||
{ id: 'pushup', name: 'Отжимания от пола', type: ExerciseType.BODYWEIGHT, bodyWeightPercentage: 65 },
|
||||
{ id: 'air_sq', name: 'Приседания (свой вес)', type: ExerciseType.BODYWEIGHT, bodyWeightPercentage: 75 },
|
||||
];
|
||||
|
||||
export const getSessions = (userId: string): WorkoutSession[] => {
|
||||
export const getSessions = async (userId: string): Promise<WorkoutSession[]> => {
|
||||
try {
|
||||
const data = localStorage.getItem(getKey(SESSIONS_KEY, userId));
|
||||
return data ? JSON.parse(data) : [];
|
||||
} catch (e) {
|
||||
return [];
|
||||
}
|
||||
};
|
||||
|
||||
export const saveSession = (userId: string, session: WorkoutSession): void => {
|
||||
const sessions = getSessions(userId);
|
||||
const index = sessions.findIndex(s => s.id === session.id);
|
||||
if (index >= 0) {
|
||||
sessions[index] = session;
|
||||
} else {
|
||||
sessions.unshift(session);
|
||||
}
|
||||
localStorage.setItem(getKey(SESSIONS_KEY, userId), JSON.stringify(sessions));
|
||||
|
||||
// Auto-update user weight profile if present in session
|
||||
if (session.userBodyWeight) {
|
||||
updateUserProfile(userId, { weight: session.userBodyWeight });
|
||||
}
|
||||
};
|
||||
|
||||
export const deleteSession = (userId: string, id: string): void => {
|
||||
let sessions = getSessions(userId);
|
||||
sessions = sessions.filter(s => s.id !== id);
|
||||
localStorage.setItem(getKey(SESSIONS_KEY, userId), JSON.stringify(sessions));
|
||||
};
|
||||
|
||||
export const deleteAllUserData = (userId: string) => {
|
||||
localStorage.removeItem(getKey(SESSIONS_KEY, userId));
|
||||
localStorage.removeItem(getKey(EXERCISES_KEY, userId));
|
||||
localStorage.removeItem(getKey(PLANS_KEY, userId));
|
||||
};
|
||||
|
||||
export const getExercises = (userId: string): ExerciseDef[] => {
|
||||
try {
|
||||
const data = localStorage.getItem(getKey(EXERCISES_KEY, userId));
|
||||
const savedExercises: ExerciseDef[] = data ? JSON.parse(data) : [];
|
||||
|
||||
// Create a map of saved exercises for easy lookup
|
||||
const savedMap = new Map(savedExercises.map(ex => [ex.id, ex]));
|
||||
|
||||
// Start with defaults
|
||||
const mergedExercises = DEFAULT_EXERCISES.map(defEx => {
|
||||
// If user has a saved version of this default exercise (e.g. edited or archived), use that
|
||||
if (savedMap.has(defEx.id)) {
|
||||
const saved = savedMap.get(defEx.id)!;
|
||||
savedMap.delete(defEx.id); // Remove from map so we don't add it again
|
||||
return saved;
|
||||
}
|
||||
return defEx;
|
||||
});
|
||||
|
||||
// Add remaining custom exercises (those that are not overrides of defaults)
|
||||
return [...mergedExercises, ...Array.from(savedMap.values())];
|
||||
} catch (e) {
|
||||
return DEFAULT_EXERCISES;
|
||||
}
|
||||
};
|
||||
|
||||
export const saveExercise = (userId: string, exercise: ExerciseDef): void => {
|
||||
try {
|
||||
const data = localStorage.getItem(getKey(EXERCISES_KEY, userId));
|
||||
let list: ExerciseDef[] = data ? JSON.parse(data) : [];
|
||||
|
||||
const index = list.findIndex(e => e.id === exercise.id);
|
||||
if (index >= 0) {
|
||||
list[index] = exercise;
|
||||
} else {
|
||||
list.push(exercise);
|
||||
}
|
||||
localStorage.setItem(getKey(EXERCISES_KEY, userId), JSON.stringify(list));
|
||||
} catch {}
|
||||
};
|
||||
|
||||
export const getLastSetForExercise = (userId: string, exerciseId: string): WorkoutSet | undefined => {
|
||||
const sessions = getSessions(userId);
|
||||
for (const session of sessions) {
|
||||
for (let i = session.sets.length - 1; i >= 0; i--) {
|
||||
if (session.sets[i].exerciseId === exerciseId) {
|
||||
return session.sets[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export const getPlans = (userId: string): WorkoutPlan[] => {
|
||||
try {
|
||||
const data = localStorage.getItem(getKey(PLANS_KEY, userId));
|
||||
return data ? JSON.parse(data) : [];
|
||||
return await api.get('/sessions');
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
};
|
||||
|
||||
export const savePlan = (userId: string, plan: WorkoutPlan): void => {
|
||||
const plans = getPlans(userId);
|
||||
const index = plans.findIndex(p => p.id === plan.id);
|
||||
if (index >= 0) {
|
||||
plans[index] = plan;
|
||||
} else {
|
||||
plans.push(plan);
|
||||
}
|
||||
localStorage.setItem(getKey(PLANS_KEY, userId), JSON.stringify(plans));
|
||||
export const saveSession = async (userId: string, session: WorkoutSession): Promise<void> => {
|
||||
await api.post('/sessions', session);
|
||||
};
|
||||
|
||||
export const deletePlan = (userId: string, id: string): void => {
|
||||
const plans = getPlans(userId).filter(p => p.id !== id);
|
||||
localStorage.setItem(getKey(PLANS_KEY, userId), JSON.stringify(plans));
|
||||
export const deleteSession = async (userId: string, id: string): Promise<void> => {
|
||||
await api.delete(`/sessions/${id}`);
|
||||
};
|
||||
|
||||
export const deleteAllUserData = (userId: string) => {
|
||||
// Not implemented in frontend
|
||||
};
|
||||
|
||||
export const getExercises = async (userId: string): Promise<ExerciseDef[]> => {
|
||||
try {
|
||||
return await api.get('/exercises');
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
};
|
||||
|
||||
export const saveExercise = async (userId: string, exercise: ExerciseDef): Promise<void> => {
|
||||
await api.post('/exercises', exercise);
|
||||
};
|
||||
|
||||
export const getLastSetForExercise = async (userId: string, exerciseId: string): Promise<WorkoutSet | undefined> => {
|
||||
// This requires fetching sessions or a specific endpoint.
|
||||
// For performance, we should probably have an endpoint for this.
|
||||
// For now, let's fetch sessions and find it client side, or implement endpoint later.
|
||||
// Given the async nature, we need to change the signature to Promise.
|
||||
// The caller needs to await this.
|
||||
const sessions = await getSessions(userId);
|
||||
for (const session of sessions) {
|
||||
for (let i = session.sets.length - 1; i >= 0; i--) {
|
||||
if (session.sets[i].exerciseId === exerciseId) {
|
||||
return session.sets[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export const getPlans = async (userId: string): Promise<WorkoutPlan[]> => {
|
||||
try {
|
||||
return await api.get('/plans');
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
};
|
||||
|
||||
export const savePlan = async (userId: string, plan: WorkoutPlan): Promise<void> => {
|
||||
await api.post('/plans', plan);
|
||||
};
|
||||
|
||||
export const deletePlan = async (userId: string, id: string): Promise<void> => {
|
||||
await api.delete(`/plans/${id}`);
|
||||
};
|
||||
Reference in New Issue
Block a user