fitness: add GPX upload with map, pace chart, and km splits
All checks were successful
CI / update (push) Successful in 2m18s
All checks were successful
CI / update (push) Successful in 2m18s
Add GPX file upload for cardio exercises in workout history. Parses GPX track points and stores them in the session. Shows route map (Leaflet), pace-over-distance chart (Chart.js), and per-km splits table with color-coded fast/slow pacing. Auto-fills distance and duration on single-set exercises. Disables Chart.js animations.
This commit is contained in:
@@ -10,12 +10,22 @@ export interface ICompletedSet {
|
||||
notes?: string;
|
||||
}
|
||||
|
||||
export interface IGpsPoint {
|
||||
lat: number;
|
||||
lng: number;
|
||||
altitude?: number;
|
||||
speed?: number;
|
||||
timestamp: number;
|
||||
}
|
||||
|
||||
export interface ICompletedExercise {
|
||||
exerciseId: string;
|
||||
name: string;
|
||||
sets: ICompletedSet[];
|
||||
restTime?: number;
|
||||
notes?: string;
|
||||
gpsTrack?: IGpsPoint[];
|
||||
totalDistance?: number; // km
|
||||
}
|
||||
|
||||
export interface IWorkoutSession {
|
||||
@@ -70,6 +80,14 @@ const CompletedSetSchema = new mongoose.Schema({
|
||||
}
|
||||
});
|
||||
|
||||
const GpsPointSchema = new mongoose.Schema({
|
||||
lat: { type: Number, required: true },
|
||||
lng: { type: Number, required: true },
|
||||
altitude: Number,
|
||||
speed: Number,
|
||||
timestamp: { type: Number, required: true }
|
||||
}, { _id: false });
|
||||
|
||||
const CompletedExerciseSchema = new mongoose.Schema({
|
||||
exerciseId: {
|
||||
type: String,
|
||||
@@ -95,6 +113,14 @@ const CompletedExerciseSchema = new mongoose.Schema({
|
||||
type: String,
|
||||
trim: true,
|
||||
maxlength: 500
|
||||
},
|
||||
gpsTrack: {
|
||||
type: [GpsPointSchema],
|
||||
default: undefined
|
||||
},
|
||||
totalDistance: {
|
||||
type: Number,
|
||||
min: 0
|
||||
}
|
||||
});
|
||||
|
||||
@@ -157,4 +183,4 @@ const WorkoutSessionSchema = new mongoose.Schema(
|
||||
WorkoutSessionSchema.index({ createdBy: 1, startTime: -1 });
|
||||
WorkoutSessionSchema.index({ templateId: 1 });
|
||||
|
||||
export const WorkoutSession = mongoose.model<IWorkoutSession>("WorkoutSession", WorkoutSessionSchema);
|
||||
export const WorkoutSession = mongoose.models.WorkoutSession as mongoose.Model<IWorkoutSession> ?? mongoose.model<IWorkoutSession>("WorkoutSession", WorkoutSessionSchema);
|
||||
Reference in New Issue
Block a user