fitness: add GPX upload with map, pace chart, and km splits
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:
2026-03-20 13:30:47 +01:00
parent 0e5d6dceb9
commit 6835f5479e
6 changed files with 627 additions and 2 deletions

View File

@@ -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);