@dgpholdings/greatoak-shared 1.1.60 → 1.1.62

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -6,342 +6,405 @@ const scoring_utils_1 = require("./scoring.utils");
6
6
  * Calculate comprehensive workout summary
7
7
  */
8
8
  const calculateWorkoutSummary = (exercises, userProfile) => {
9
- var _a, _b;
10
- let totalScore = 0;
11
- let totalCalories = 0;
12
- let totalEpocCalories = 0;
13
- let totalSets = 0;
14
- const exerciseSummaries = [];
15
- const muscleStressMap = new Map();
16
- // Calculate workout duration
17
- const workoutStart = ((_a = exercises[0]) === null || _a === void 0 ? void 0 : _a.startTime) || new Date();
18
- const workoutEnd = ((_b = exercises[exercises.length - 1]) === null || _b === void 0 ? void 0 : _b.endTime) || new Date();
19
- const workoutDuration = Math.max(1, (workoutEnd.getTime() - workoutStart.getTime()) / (1000 * 60));
20
- // Process each exercise
21
- exercises.forEach((exerciseData) => {
22
- const { exercise, records } = exerciseData;
23
- const sets = [];
24
- let exerciseScore = 0;
25
- let exerciseCalories = 0;
26
- let bestSetScore = 0;
27
- let bestSetNumber = 1;
28
- let bestSetReason = "highest score";
29
- // Process each set/record
30
- records.forEach((record, index) => {
31
- if (!record.isDone)
32
- return;
33
- const scoreBreakdown = (0, scoring_utils_1.computeScoreFromRecord)({
34
- record,
35
- exercise,
36
- userProfile,
37
- isTimeIntervalModeEnabled: !!(record.workDurationSecs && record.restDurationSecs),
38
- });
39
- const setNumber = index + 1;
40
- const setDescription = generateSetDescription(record, exercise, scoreBreakdown);
41
- sets.push({
42
- setNumber,
43
- record,
44
- scoreBreakdown,
45
- description: setDescription,
46
- });
47
- exerciseScore += scoreBreakdown.finalScore;
48
- exerciseCalories += scoreBreakdown.caloriesBurned;
49
- totalSets++;
50
- // Track best set
51
- if (scoreBreakdown.finalScore > bestSetScore) {
52
- bestSetScore = scoreBreakdown.finalScore;
53
- bestSetNumber = setNumber;
54
- bestSetReason = getBestSetReason(record, exercise);
55
- }
56
- // Accumulate muscle stress
57
- exercise.primaryMuscles.forEach((muscle) => {
58
- const currentStress = muscleStressMap.get(muscle) || 0;
59
- muscleStressMap.set(muscle, currentStress + scoreBreakdown.finalScore * 1.0); // Primary muscles get full stress
60
- });
61
- exercise.secondaryMuscles.forEach((muscle) => {
62
- const currentStress = muscleStressMap.get(muscle) || 0;
63
- muscleStressMap.set(muscle, currentStress + scoreBreakdown.finalScore * 0.6); // Secondary muscles get 60% stress
64
- });
65
- });
66
- if (sets.length > 0) {
67
- exerciseSummaries.push({
68
- exercise,
69
- sets,
70
- totalScore: exerciseScore,
71
- averageScore: exerciseScore / sets.length,
72
- totalCalories: exerciseCalories,
73
- bestSet: {
74
- setNumber: bestSetNumber,
75
- score: bestSetScore,
76
- reason: bestSetReason,
77
- },
78
- muscleActivation: calculateMuscleActivation(exercise, sets),
79
- });
80
- }
9
+ var _a, _b;
10
+ let totalScore = 0;
11
+ let totalCalories = 0;
12
+ let totalEpocCalories = 0;
13
+ let totalSets = 0;
14
+ const exerciseSummaries = [];
15
+ const muscleStressMap = new Map();
16
+ // Calculate workout duration
17
+ const workoutStart =
18
+ ((_a = exercises[0]) === null || _a === void 0 ? void 0 : _a.startTime) ||
19
+ new Date();
20
+ const workoutEnd =
21
+ ((_b = exercises[exercises.length - 1]) === null || _b === void 0
22
+ ? void 0
23
+ : _b.endTime) || new Date();
24
+ const workoutDuration = Math.max(
25
+ 1,
26
+ (workoutEnd.getTime() - workoutStart.getTime()) / (1000 * 60)
27
+ );
28
+ // Process each exercise
29
+ exercises.forEach((exerciseData) => {
30
+ const { exercise, records } = exerciseData;
31
+ const sets = [];
32
+ let exerciseScore = 0;
33
+ let exerciseCalories = 0;
34
+ let bestSetScore = 0;
35
+ let bestSetNumber = 1;
36
+ let bestSetReason = "highest score";
37
+ // Process each set/record
38
+ records.forEach((record, index) => {
39
+ if (!record.isDone) return;
40
+ const scoreBreakdown = (0, scoring_utils_1.computeScoreFromRecord)({
41
+ record,
42
+ exercise,
43
+ userProfile,
44
+ isTimeIntervalModeEnabled: !!(
45
+ record.workDurationSecs && record.restDurationSecs
46
+ ),
47
+ });
48
+ const setNumber = index + 1;
49
+ const setDescription = generateSetDescription(
50
+ record,
51
+ exercise,
52
+ scoreBreakdown
53
+ );
54
+ sets.push({
55
+ setNumber,
56
+ record,
57
+ scoreBreakdown,
58
+ description: setDescription,
59
+ });
60
+ exerciseScore += scoreBreakdown.finalScore;
61
+ exerciseCalories += scoreBreakdown.caloriesBurned;
62
+ totalSets++;
63
+ // Track best set
64
+ if (scoreBreakdown.finalScore > bestSetScore) {
65
+ bestSetScore = scoreBreakdown.finalScore;
66
+ bestSetNumber = setNumber;
67
+ bestSetReason = getBestSetReason(record, exercise);
68
+ }
69
+ // Accumulate muscle stress
70
+ exercise.primaryMuscles.forEach((muscle) => {
71
+ const currentStress = muscleStressMap.get(muscle) || 0;
72
+ muscleStressMap.set(
73
+ muscle,
74
+ currentStress + scoreBreakdown.finalScore * 1.0
75
+ ); // Primary muscles get full stress
76
+ });
77
+ exercise.secondaryMuscles.forEach((muscle) => {
78
+ const currentStress = muscleStressMap.get(muscle) || 0;
79
+ muscleStressMap.set(
80
+ muscle,
81
+ currentStress + scoreBreakdown.finalScore * 0.6
82
+ ); // Secondary muscles get 60% stress
83
+ });
81
84
  });
82
- // Calculate totals
83
- totalScore = exerciseSummaries.reduce((sum, ex) => sum + ex.totalScore, 0);
84
- totalCalories = exerciseSummaries.reduce((sum, ex) => sum + ex.totalCalories, 0);
85
- totalEpocCalories = exerciseSummaries.reduce((sum, ex) => sum +
86
- ex.sets.reduce((setSum, set) => setSum + set.scoreBreakdown.epocCalories, 0), 0);
87
- // Calculate muscle recovery
88
- const muscleRecovery = calculateMuscleRecovery(muscleStressMap, exercises);
89
- // Determine overall fatigue level
90
- const averageScore = totalSets > 0 ? totalScore / totalSets : 0;
91
- const fatigueLevel = getFatigueLevel(averageScore, totalScore, workoutDuration, userProfile);
92
- const recommendedRestDays = getRecommendedRestDays(fatigueLevel, muscleRecovery);
93
- return {
94
- totalScore: Math.round(totalScore),
95
- averageScore: Math.round(averageScore),
96
- totalCalories: Math.round(totalCalories),
97
- totalEpocCalories: Math.round(totalEpocCalories),
98
- workoutDuration: Math.round(workoutDuration),
99
- exerciseSummaries,
100
- muscleRecovery,
101
- fatigueLevel,
102
- recommendedRestDays,
103
- };
85
+ if (sets.length > 0) {
86
+ exerciseSummaries.push({
87
+ exercise,
88
+ sets,
89
+ totalScore: exerciseScore,
90
+ averageScore: exerciseScore / sets.length,
91
+ totalCalories: exerciseCalories,
92
+ bestSet: {
93
+ setNumber: bestSetNumber,
94
+ score: bestSetScore,
95
+ reason: bestSetReason,
96
+ },
97
+ muscleActivation: calculateMuscleActivation(exercise, sets),
98
+ });
99
+ }
100
+ });
101
+ // Calculate totals
102
+ totalScore = exerciseSummaries.reduce((sum, ex) => sum + ex.totalScore, 0);
103
+ totalCalories = exerciseSummaries.reduce(
104
+ (sum, ex) => sum + ex.totalCalories,
105
+ 0
106
+ );
107
+ totalEpocCalories = exerciseSummaries.reduce(
108
+ (sum, ex) =>
109
+ sum +
110
+ ex.sets.reduce(
111
+ (setSum, set) => setSum + set.scoreBreakdown.epocCalories,
112
+ 0
113
+ ),
114
+ 0
115
+ );
116
+ // Calculate muscle recovery
117
+ const muscleRecovery = calculateMuscleRecovery(muscleStressMap, exercises);
118
+ // Determine overall fatigue level
119
+ const averageScore = totalSets > 0 ? totalScore / totalSets : 0;
120
+ const fatigueLevel = getFatigueLevel(
121
+ averageScore,
122
+ totalScore,
123
+ workoutDuration,
124
+ userProfile
125
+ );
126
+ const recommendedRestDays = getRecommendedRestDays(
127
+ fatigueLevel,
128
+ muscleRecovery
129
+ );
130
+ return {
131
+ totalScore: Math.round(totalScore),
132
+ averageScore: Math.round(averageScore),
133
+ totalCalories: Math.round(totalCalories),
134
+ totalEpocCalories: Math.round(totalEpocCalories),
135
+ workoutDuration: Math.round(workoutDuration),
136
+ exerciseSummaries,
137
+ muscleRecovery,
138
+ fatigueLevel,
139
+ recommendedRestDays,
140
+ };
104
141
  };
105
142
  exports.calculateWorkoutSummary = calculateWorkoutSummary;
106
143
  /**
107
144
  * Generate human-readable set description
108
145
  */
109
146
  const generateSetDescription = (record, exercise, scoreBreakdown) => {
110
- var _a;
111
- const effort = record.rpe
112
- ? `RPE ${record.rpe}`
113
- : "rir" in record && record.rir
114
- ? `${record.rir} RIR`
115
- : "";
116
- const calories = `${Math.round(scoreBreakdown.caloriesBurned)} cal`;
117
- switch (record.type) {
118
- case "weight-reps":
119
- return `${record.kg}kg × ${record.reps} reps ${effort} • ${calories}`;
120
- case "reps-only":
121
- const auxWeight = record.auxWeightKg ? ` (+${record.auxWeightKg}kg)` : "";
122
- return `${record.reps} reps${auxWeight} ${effort} • ${calories}`;
123
- case "duration":
124
- const auxWeightDur = record.auxWeightKg
125
- ? ` (+${record.auxWeightKg}kg)`
126
- : "";
127
- const minutes = Math.floor(record.durationSecs / 60);
128
- const seconds = record.durationSecs % 60;
129
- const timeStr = minutes > 0
130
- ? `${minutes}:${seconds < 10 ? `0${seconds}` : `${seconds}`}`
131
- : `${seconds}s`;
132
- return `${timeStr}${auxWeightDur} ${effort} • ${calories}`;
133
- case "cardio-machine":
134
- const rpe = (_a = record.rpe) !== null && _a !== void 0 ? _a : 1;
135
- const resistance = rpe > 0 ? ` R${rpe}` : "";
136
- const minutes_cm = Math.floor(record.durationSecs / 60);
137
- const seconds_cm = record.durationSecs % 60;
138
- const timeStr_cm = minutes_cm > 0
139
- ? `${minutes_cm}:${seconds_cm < 10 ? `0${seconds_cm}` : `${seconds_cm}`}`
140
- : `${seconds_cm}s`;
141
- return `${record.speed} speed${resistance} × ${timeStr_cm} ${effort} • ${calories}`;
142
- case "cardio-free":
143
- const minutes_cf = Math.floor(record.durationSecs / 60);
144
- const seconds_cf = record.durationSecs % 60;
145
- const timeStr_cf = minutes_cf > 0
146
- ? `${minutes_cf}:${seconds_cf < 10 ? `0${seconds_cf}` : `${seconds_cf}`}`
147
- : `${seconds_cf}s`;
148
- let pace = "";
149
- if ("avgPaceSecsPerKm" in record && record.avgPaceSecsPerKm) {
150
- const paceMinutes = Math.floor(record.avgPaceSecsPerKm / 60);
151
- const paceSeconds = Math.round(record.avgPaceSecsPerKm % 60);
152
- const paceSecondsFormatted = paceSeconds < 10 ? `0${paceSeconds}` : `${paceSeconds}`;
153
- pace = ` (${paceMinutes}:${paceSecondsFormatted}/km)`;
154
- }
155
- return `${record.distance}km in ${timeStr_cf}${pace} ${effort} • ${calories}`;
156
- default:
157
- return `${effort} ${calories}`;
158
- }
147
+ var _a;
148
+ const effort = record.rpe
149
+ ? `RPE ${record.rpe}`
150
+ : "rir" in record && record.rir
151
+ ? `${record.rir} RIR`
152
+ : "";
153
+ const calories = `${Math.round(scoreBreakdown.caloriesBurned)} cal`;
154
+ switch (record.type) {
155
+ case "weight-reps":
156
+ return `${record.kg}kg × ${record.reps} reps ${effort} • ${calories}`;
157
+ case "reps-only":
158
+ const auxWeight = record.auxWeightKg ? ` (+${record.auxWeightKg}kg)` : "";
159
+ return `${record.reps} reps${auxWeight} ${effort} • ${calories}`;
160
+ case "duration":
161
+ const auxWeightDur = record.auxWeightKg
162
+ ? ` (+${record.auxWeightKg}kg)`
163
+ : "";
164
+ const minutes = Math.floor(record.durationMmSs / 60);
165
+ const seconds = record.durationMmSs % 60;
166
+ const timeStr =
167
+ minutes > 0
168
+ ? `${minutes}:${seconds < 10 ? `0${seconds}` : `${seconds}`}`
169
+ : `${seconds}s`;
170
+ return `${timeStr}${auxWeightDur} ${effort} • ${calories}`;
171
+ case "cardio-machine":
172
+ const rpe = (_a = record.rpe) !== null && _a !== void 0 ? _a : 1;
173
+ const resistance = rpe > 0 ? ` R${rpe}` : "";
174
+ const minutes_cm = Math.floor(record.durationMmSs / 60);
175
+ const seconds_cm = record.durationMmSs % 60;
176
+ const timeStr_cm =
177
+ minutes_cm > 0
178
+ ? `${minutes_cm}:${
179
+ seconds_cm < 10 ? `0${seconds_cm}` : `${seconds_cm}`
180
+ }`
181
+ : `${seconds_cm}s`;
182
+ return `${record.speed} speed${resistance} × ${timeStr_cm} ${effort} • ${calories}`;
183
+ case "cardio-free":
184
+ const minutes_cf = Math.floor(record.durationMmSs / 60);
185
+ const seconds_cf = record.durationMmSs % 60;
186
+ const timeStr_cf =
187
+ minutes_cf > 0
188
+ ? `${minutes_cf}:${
189
+ seconds_cf < 10 ? `0${seconds_cf}` : `${seconds_cf}`
190
+ }`
191
+ : `${seconds_cf}s`;
192
+ let pace = "";
193
+ if ("avgPaceSecsPerKm" in record && record.avgPaceSecsPerKm) {
194
+ const paceMinutes = Math.floor(record.avgPaceSecsPerKm / 60);
195
+ const paceSeconds = Math.round(record.avgPaceSecsPerKm % 60);
196
+ const paceSecondsFormatted =
197
+ paceSeconds < 10 ? `0${paceSeconds}` : `${paceSeconds}`;
198
+ pace = ` (${paceMinutes}:${paceSecondsFormatted}/km)`;
199
+ }
200
+ return `${record.distance}km in ${timeStr_cf}${pace} ${effort} • ${calories}`;
201
+ default:
202
+ return `${effort} • ${calories}`;
203
+ }
159
204
  };
160
205
  /**
161
206
  * Determine reason for best set
162
207
  */
163
208
  const getBestSetReason = (record, exercise) => {
164
- switch (record.type) {
165
- case "weight-reps":
166
- return "heaviest weight";
167
- case "reps-only":
168
- return "most reps";
169
- case "duration":
170
- return "longest hold";
171
- case "cardio-machine":
172
- return "highest intensity";
173
- case "cardio-free":
174
- return "fastest pace";
175
- default:
176
- return "highest effort";
177
- }
209
+ switch (record.type) {
210
+ case "weight-reps":
211
+ return "heaviest weight";
212
+ case "reps-only":
213
+ return "most reps";
214
+ case "duration":
215
+ return "longest hold";
216
+ case "cardio-machine":
217
+ return "highest intensity";
218
+ case "cardio-free":
219
+ return "fastest pace";
220
+ default:
221
+ return "highest effort";
222
+ }
178
223
  };
179
224
  /**
180
225
  * Calculate muscle activation levels
181
226
  */
182
227
  const calculateMuscleActivation = (exercise, sets) => {
183
- if (sets.length === 0)
184
- return { primary: 0, secondary: 0 };
185
- const avgScore = sets.reduce((sum, set) => sum + set.scoreBreakdown.finalScore, 0) /
186
- sets.length;
187
- const maxPossibleScore = 100; // Reference point
188
- // Activation based on exercise difficulty and average set score
189
- const baseActivation = Math.min(100, (avgScore / maxPossibleScore) * 100);
190
- const difficultyBonus = exercise.difficultyLevel * 2; // Up to 20% bonus
191
- return {
192
- primary: Math.min(100, Math.round(baseActivation + difficultyBonus)),
193
- secondary: Math.min(100, Math.round((baseActivation + difficultyBonus) * 0.7)), // 70% of primary
194
- };
228
+ if (sets.length === 0) return { primary: 0, secondary: 0 };
229
+ const avgScore =
230
+ sets.reduce((sum, set) => sum + set.scoreBreakdown.finalScore, 0) /
231
+ sets.length;
232
+ const maxPossibleScore = 100; // Reference point
233
+ // Activation based on exercise difficulty and average set score
234
+ const baseActivation = Math.min(100, (avgScore / maxPossibleScore) * 100);
235
+ const difficultyBonus = exercise.difficultyLevel * 2; // Up to 20% bonus
236
+ return {
237
+ primary: Math.min(100, Math.round(baseActivation + difficultyBonus)),
238
+ secondary: Math.min(
239
+ 100,
240
+ Math.round((baseActivation + difficultyBonus) * 0.7)
241
+ ), // 70% of primary
242
+ };
195
243
  };
196
244
  /**
197
245
  * Calculate muscle recovery times
198
246
  */
199
247
  const calculateMuscleRecovery = (muscleStressMap, exercises) => {
200
- const recovery = {};
201
- muscleStressMap.forEach((totalStress, muscle) => {
202
- // Base recovery time based on muscle group
203
- const baseRecovery = getMuscleBaseRecovery(muscle);
204
- // Stress level (0-100)
205
- const fatigueLevel = Math.min(100, (totalStress / 200) * 100); // 200 = very high stress threshold
206
- // Recovery time increases with fatigue level
207
- let recoveryHours = baseRecovery;
208
- if (fatigueLevel > 80)
209
- recoveryHours *= 1.5; // 50% longer for high fatigue
210
- else if (fatigueLevel > 60)
211
- recoveryHours *= 1.3; // 30% longer for moderate-high fatigue
212
- else if (fatigueLevel > 40)
213
- recoveryHours *= 1.1; // 10% longer for moderate fatigue
214
- // Find exercises that worked this muscle - using manual search instead of includes
215
- const involvedExercises = [];
216
- exercises.forEach(({ exercise }) => {
217
- let found = false;
218
- // Check primary muscles
219
- for (let i = 0; i < exercise.primaryMuscles.length; i++) {
220
- if (exercise.primaryMuscles[i] === muscle) {
221
- found = true;
222
- break;
223
- }
224
- }
225
- // Check secondary muscles if not found in primary
226
- if (!found) {
227
- for (let i = 0; i < exercise.secondaryMuscles.length; i++) {
228
- if (exercise.secondaryMuscles[i] === muscle) {
229
- found = true;
230
- break;
231
- }
232
- }
233
- }
234
- if (found) {
235
- involvedExercises.push(exercise.name);
236
- }
237
- });
238
- recovery[muscle] = {
239
- fatigueLevel: Math.round(fatigueLevel),
240
- recoveryHours: Math.round(recoveryHours),
241
- trainingStress: Math.round(totalStress),
242
- exercises: involvedExercises,
243
- };
248
+ const recovery = {};
249
+ muscleStressMap.forEach((totalStress, muscle) => {
250
+ // Base recovery time based on muscle group
251
+ const baseRecovery = getMuscleBaseRecovery(muscle);
252
+ // Stress level (0-100)
253
+ const fatigueLevel = Math.min(100, (totalStress / 200) * 100); // 200 = very high stress threshold
254
+ // Recovery time increases with fatigue level
255
+ let recoveryHours = baseRecovery;
256
+ if (fatigueLevel > 80) recoveryHours *= 1.5; // 50% longer for high fatigue
257
+ else if (fatigueLevel > 60)
258
+ recoveryHours *= 1.3; // 30% longer for moderate-high fatigue
259
+ else if (fatigueLevel > 40) recoveryHours *= 1.1; // 10% longer for moderate fatigue
260
+ // Find exercises that worked this muscle - using manual search instead of includes
261
+ const involvedExercises = [];
262
+ exercises.forEach(({ exercise }) => {
263
+ let found = false;
264
+ // Check primary muscles
265
+ for (let i = 0; i < exercise.primaryMuscles.length; i++) {
266
+ if (exercise.primaryMuscles[i] === muscle) {
267
+ found = true;
268
+ break;
269
+ }
270
+ }
271
+ // Check secondary muscles if not found in primary
272
+ if (!found) {
273
+ for (let i = 0; i < exercise.secondaryMuscles.length; i++) {
274
+ if (exercise.secondaryMuscles[i] === muscle) {
275
+ found = true;
276
+ break;
277
+ }
278
+ }
279
+ }
280
+ if (found) {
281
+ involvedExercises.push(exercise.name);
282
+ }
244
283
  });
245
- return recovery;
284
+ recovery[muscle] = {
285
+ fatigueLevel: Math.round(fatigueLevel),
286
+ recoveryHours: Math.round(recoveryHours),
287
+ trainingStress: Math.round(totalStress),
288
+ exercises: involvedExercises,
289
+ };
290
+ });
291
+ return recovery;
246
292
  };
247
293
  /**
248
294
  * Get base recovery time for different muscle groups
249
295
  */
250
296
  const getMuscleBaseRecovery = (muscle) => {
251
- // Recovery times in hours based on muscle size and recovery capacity
252
- const recoveryTimes = {
253
- "pectoralis-major": 48,
254
- "pectoralis-minor": 48,
255
- "latissimus-dorsi": 48,
256
- trapezius: 36,
257
- rhomboids: 36,
258
- "deltoids-anterior": 36,
259
- "deltoids-middle": 36,
260
- "bicep-short-inner": 24,
261
- "bicep-long-outer": 24,
262
- "tricep-brachii-long": 24,
263
- "tricep-brachii-lateral": 24,
264
- quadriceps: 48,
265
- hamstrings: 48,
266
- "maximus-lower": 48,
267
- "medius-upper": 36,
268
- "calf-inner": 24,
269
- "calf-outer": 24,
270
- "abs-upper": 24,
271
- "abs-lower": 24,
272
- obliques: 24,
273
- "lower-back": 48,
274
- "fore-arm-inner": 18,
275
- "fore-arm-outer": 18,
276
- };
277
- return recoveryTimes[muscle] || 36; // Default 36 hours
297
+ // Recovery times in hours based on muscle size and recovery capacity
298
+ const recoveryTimes = {
299
+ "pectoralis-major": 48,
300
+ "pectoralis-minor": 48,
301
+ "latissimus-dorsi": 48,
302
+ trapezius: 36,
303
+ rhomboids: 36,
304
+ "deltoids-anterior": 36,
305
+ "deltoids-middle": 36,
306
+ "bicep-short-inner": 24,
307
+ "bicep-long-outer": 24,
308
+ "tricep-brachii-long": 24,
309
+ "tricep-brachii-lateral": 24,
310
+ quadriceps: 48,
311
+ hamstrings: 48,
312
+ "maximus-lower": 48,
313
+ "medius-upper": 36,
314
+ "calf-inner": 24,
315
+ "calf-outer": 24,
316
+ "abs-upper": 24,
317
+ "abs-lower": 24,
318
+ obliques: 24,
319
+ "lower-back": 48,
320
+ "fore-arm-inner": 18,
321
+ "fore-arm-outer": 18,
322
+ };
323
+ return recoveryTimes[muscle] || 36; // Default 36 hours
278
324
  };
279
325
  /**
280
326
  * Determine overall fatigue level
281
327
  */
282
- const getFatigueLevel = (averageScore, totalScore, workoutDuration, userProfile) => {
283
- const fitnessAdjustment = (userProfile === null || userProfile === void 0 ? void 0 : userProfile.fitnessLevel)
284
- ? (userProfile.fitnessLevel - 3) * 0.1 // ±20% based on fitness level
285
- : 0;
286
- const intensity = averageScore * (1 - fitnessAdjustment);
287
- const volume = totalScore / 100; // Normalize volume
288
- const density = totalScore / workoutDuration; // Score per minute
289
- if (intensity > 80 || volume > 15 || density > 8)
290
- return "extreme";
291
- if (intensity > 65 || volume > 12 || density > 6)
292
- return "very_hard";
293
- if (intensity > 50 || volume > 8 || density > 4)
294
- return "hard";
295
- if (intensity > 35 || volume > 5 || density > 2)
296
- return "moderate";
297
- return "light";
328
+ const getFatigueLevel = (
329
+ averageScore,
330
+ totalScore,
331
+ workoutDuration,
332
+ userProfile
333
+ ) => {
334
+ const fitnessAdjustment = (
335
+ userProfile === null || userProfile === void 0
336
+ ? void 0
337
+ : userProfile.fitnessLevel
338
+ )
339
+ ? (userProfile.fitnessLevel - 3) * 0.1 // ±20% based on fitness level
340
+ : 0;
341
+ const intensity = averageScore * (1 - fitnessAdjustment);
342
+ const volume = totalScore / 100; // Normalize volume
343
+ const density = totalScore / workoutDuration; // Score per minute
344
+ if (intensity > 80 || volume > 15 || density > 8) return "extreme";
345
+ if (intensity > 65 || volume > 12 || density > 6) return "very_hard";
346
+ if (intensity > 50 || volume > 8 || density > 4) return "hard";
347
+ if (intensity > 35 || volume > 5 || density > 2) return "moderate";
348
+ return "light";
298
349
  };
299
350
  /**
300
351
  * Get recommended rest days based on fatigue
301
352
  */
302
353
  const getRecommendedRestDays = (fatigueLevel, muscleRecovery) => {
303
- // Base rest days by fatigue level
304
- const baseDays = {
305
- light: 0,
306
- moderate: 1,
307
- hard: 1,
308
- very_hard: 2,
309
- extreme: 3,
310
- };
311
- const restDays = baseDays[fatigueLevel];
312
- // Consider muscle recovery times - using manual iteration
313
- const recoveryHours = [];
314
- for (const muscle in muscleRecovery) {
315
- if (muscleRecovery.hasOwnProperty(muscle)) {
316
- recoveryHours.push(muscleRecovery[muscle].recoveryHours);
317
- }
354
+ // Base rest days by fatigue level
355
+ const baseDays = {
356
+ light: 0,
357
+ moderate: 1,
358
+ hard: 1,
359
+ very_hard: 2,
360
+ extreme: 3,
361
+ };
362
+ const restDays = baseDays[fatigueLevel];
363
+ // Consider muscle recovery times - using manual iteration
364
+ const recoveryHours = [];
365
+ for (const muscle in muscleRecovery) {
366
+ if (muscleRecovery.hasOwnProperty(muscle)) {
367
+ recoveryHours.push(muscleRecovery[muscle].recoveryHours);
318
368
  }
319
- let maxRecoveryHours = 0;
320
- for (let i = 0; i < recoveryHours.length; i++) {
321
- if (recoveryHours[i] > maxRecoveryHours) {
322
- maxRecoveryHours = recoveryHours[i];
323
- }
369
+ }
370
+ let maxRecoveryHours = 0;
371
+ for (let i = 0; i < recoveryHours.length; i++) {
372
+ if (recoveryHours[i] > maxRecoveryHours) {
373
+ maxRecoveryHours = recoveryHours[i];
324
374
  }
325
- const muscleBasedDays = Math.ceil(maxRecoveryHours / 24);
326
- return Math.max(restDays, muscleBasedDays);
375
+ }
376
+ const muscleBasedDays = Math.ceil(maxRecoveryHours / 24);
377
+ return Math.max(restDays, muscleBasedDays);
327
378
  };
328
379
  /**
329
380
  * Generate user-friendly workout summary text
330
381
  */
331
382
  const generateWorkoutSummaryText = (summary) => {
332
- const { totalScore, averageScore, totalCalories, totalEpocCalories, workoutDuration, fatigueLevel, recommendedRestDays, exerciseSummaries, } = summary;
333
- const intensityMap = {
334
- light: "Light workout",
335
- moderate: "Moderate workout",
336
- hard: "Hard workout",
337
- very_hard: "Very hard workout",
338
- extreme: "Extreme workout",
339
- };
340
- const intensityText = intensityMap[fatigueLevel];
341
- const restText = recommendedRestDays === 0
342
- ? "You can train again tomorrow"
343
- : `Recommended rest: ${recommendedRestDays} day${recommendedRestDays > 1 ? "s" : ""}`;
344
- return `
383
+ const {
384
+ totalScore,
385
+ averageScore,
386
+ totalCalories,
387
+ totalEpocCalories,
388
+ workoutDuration,
389
+ fatigueLevel,
390
+ recommendedRestDays,
391
+ exerciseSummaries,
392
+ } = summary;
393
+ const intensityMap = {
394
+ light: "Light workout",
395
+ moderate: "Moderate workout",
396
+ hard: "Hard workout",
397
+ very_hard: "Very hard workout",
398
+ extreme: "Extreme workout",
399
+ };
400
+ const intensityText = intensityMap[fatigueLevel];
401
+ const restText =
402
+ recommendedRestDays === 0
403
+ ? "You can train again tomorrow"
404
+ : `Recommended rest: ${recommendedRestDays} day${
405
+ recommendedRestDays > 1 ? "s" : ""
406
+ }`;
407
+ return `
345
408
  🏋️ **${intensityText}** (Score: ${totalScore})
346
409
  ⏱️ Duration: ${workoutDuration} minutes
347
410
  🔥 Calories: ${totalCalories} + ${totalEpocCalories} afterburn
@@ -349,8 +412,13 @@ const generateWorkoutSummaryText = (summary) => {
349
412
 
350
413
  💪 **Exercises completed:**
351
414
  ${exerciseSummaries
352
- .map((ex) => `• ${ex.exercise.name}: ${ex.sets.length} sets (${Math.round(ex.totalScore)} pts)`)
353
- .join("\n")}
415
+ .map(
416
+ (ex) =>
417
+ `• ${ex.exercise.name}: ${ex.sets.length} sets (${Math.round(
418
+ ex.totalScore
419
+ )} pts)`
420
+ )
421
+ .join("\n")}
354
422
 
355
423
  🛌 **Recovery:** ${restText}
356
424
  `.trim();