@burdenoff/microfe-mybodyshield 2026.720.2 → 2026.731.1
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.
- package/dist/mybodyshield/hooks/useMyBodyShieldApi.js +45 -31
- package/dist/mybodyshield/hooks/useMyBodyShieldApi.js.map +1 -1
- package/dist/mybodyshield/pages/train/SessionsPage.d.ts.map +1 -1
- package/dist/mybodyshield/pages/train/SessionsPage.js +162 -123
- package/dist/mybodyshield/pages/train/SessionsPage.js.map +1 -1
- package/package.json +4 -4
|
@@ -106,6 +106,20 @@ var a = () => {
|
|
|
106
106
|
}
|
|
107
107
|
});
|
|
108
108
|
}, w = (t) => {
|
|
109
|
+
let { apiGatewayUrl: r } = e();
|
|
110
|
+
return n({
|
|
111
|
+
queryKey: s.programWorkouts(t ?? ""),
|
|
112
|
+
enabled: !!t,
|
|
113
|
+
queryFn: async () => x((await o(`query MybProgramWorkouts($programId: ID!) {
|
|
114
|
+
mybProgramWorkouts(programId: $programId) {
|
|
115
|
+
items {
|
|
116
|
+
${d}
|
|
117
|
+
exercises { items { ${u} } }
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}`, { programId: t }, r)).mybProgramWorkouts.items)
|
|
121
|
+
});
|
|
122
|
+
}, T = (t) => {
|
|
109
123
|
let { apiGatewayUrl: r } = e();
|
|
110
124
|
return n({
|
|
111
125
|
queryKey: s.workoutExercises(t ?? ""),
|
|
@@ -116,7 +130,7 @@ var a = () => {
|
|
|
116
130
|
}
|
|
117
131
|
}`, { workoutId: t }, r)).mybWorkoutExercises.items.slice().sort((e, t) => e.orderIndex - t.orderIndex)
|
|
118
132
|
});
|
|
119
|
-
},
|
|
133
|
+
}, E = () => {
|
|
120
134
|
let { apiGatewayUrl: n } = e(), i = r();
|
|
121
135
|
return t({
|
|
122
136
|
mutationFn: async (e) => (await o(`mutation MybCreateProgram($input: MybCreateProgramInput!) {
|
|
@@ -124,7 +138,7 @@ var a = () => {
|
|
|
124
138
|
}`, { input: e }, n)).mybCreateProgram,
|
|
125
139
|
onSuccess: () => i.invalidateQueries({ queryKey: s.programs })
|
|
126
140
|
});
|
|
127
|
-
},
|
|
141
|
+
}, D = () => {
|
|
128
142
|
let { apiGatewayUrl: n } = e(), i = r();
|
|
129
143
|
return t({
|
|
130
144
|
mutationFn: async (e) => (await o(`mutation MybUpdateProgram($id: ID!, $input: MybUpdateProgramInput!) {
|
|
@@ -134,7 +148,7 @@ var a = () => {
|
|
|
134
148
|
i.invalidateQueries({ queryKey: s.programs }), i.invalidateQueries({ queryKey: s.program(e.id) });
|
|
135
149
|
}
|
|
136
150
|
});
|
|
137
|
-
},
|
|
151
|
+
}, O = () => {
|
|
138
152
|
let { apiGatewayUrl: n } = e(), i = r();
|
|
139
153
|
return t({
|
|
140
154
|
mutationFn: async (e) => (await o(`mutation MybTransitionProgram($id: ID!, $status: MybProgramStatus!) {
|
|
@@ -144,13 +158,13 @@ var a = () => {
|
|
|
144
158
|
i.invalidateQueries({ queryKey: s.programs }), i.invalidateQueries({ queryKey: s.program(e.id) });
|
|
145
159
|
}
|
|
146
160
|
});
|
|
147
|
-
},
|
|
161
|
+
}, k = () => {
|
|
148
162
|
let { apiGatewayUrl: n } = e(), i = r();
|
|
149
163
|
return t({
|
|
150
164
|
mutationFn: async (e) => (await o("mutation MybArchiveProgram($id: ID!) { mybArchiveProgram(id: $id) }", { id: e }, n)).mybArchiveProgram,
|
|
151
165
|
onSuccess: () => i.invalidateQueries({ queryKey: s.programs })
|
|
152
166
|
});
|
|
153
|
-
},
|
|
167
|
+
}, A = () => {
|
|
154
168
|
let { apiGatewayUrl: n } = e(), i = r();
|
|
155
169
|
return t({
|
|
156
170
|
mutationFn: async (e) => (await o(`mutation MybAddWorkout($input: MybCreateWorkoutInput!) {
|
|
@@ -160,7 +174,7 @@ var a = () => {
|
|
|
160
174
|
i.invalidateQueries({ queryKey: s.program(e.programId) }), i.invalidateQueries({ queryKey: s.programWorkouts(e.programId) });
|
|
161
175
|
}
|
|
162
176
|
});
|
|
163
|
-
},
|
|
177
|
+
}, j = () => {
|
|
164
178
|
let { apiGatewayUrl: n } = e(), i = r();
|
|
165
179
|
return t({
|
|
166
180
|
mutationFn: async (e) => (await o("mutation MybAddExercise($input: MybCreateExerciseInput!) {\n mybAddExercise(input: $input) { id workoutId }\n }", { input: e }, n)).mybAddExercise,
|
|
@@ -168,7 +182,7 @@ var a = () => {
|
|
|
168
182
|
i.invalidateQueries({ queryKey: ["mybodyshield", "program"] }), i.invalidateQueries({ queryKey: ["mybodyshield", "program-workouts"] });
|
|
169
183
|
}
|
|
170
184
|
});
|
|
171
|
-
},
|
|
185
|
+
}, M = (t = {}) => {
|
|
172
186
|
let { apiGatewayUrl: r } = e();
|
|
173
187
|
return n({
|
|
174
188
|
queryKey: [...s.sessions, t],
|
|
@@ -183,7 +197,7 @@ var a = () => {
|
|
|
183
197
|
pagination: t.pagination ?? c
|
|
184
198
|
}, r)).mybListSessions
|
|
185
199
|
});
|
|
186
|
-
},
|
|
200
|
+
}, N = (t) => {
|
|
187
201
|
let { apiGatewayUrl: r } = e();
|
|
188
202
|
return n({
|
|
189
203
|
queryKey: s.session(t ?? ""),
|
|
@@ -203,7 +217,7 @@ var a = () => {
|
|
|
203
217
|
};
|
|
204
218
|
}
|
|
205
219
|
});
|
|
206
|
-
},
|
|
220
|
+
}, P = (t) => {
|
|
207
221
|
let { apiGatewayUrl: r } = e(), i = t.trim();
|
|
208
222
|
return n({
|
|
209
223
|
queryKey: s.exerciseBestSet(i),
|
|
@@ -213,7 +227,7 @@ var a = () => {
|
|
|
213
227
|
}`, { exerciseName: i }, r)).mybExerciseBestSet,
|
|
214
228
|
staleTime: 15e3
|
|
215
229
|
});
|
|
216
|
-
},
|
|
230
|
+
}, F = () => {
|
|
217
231
|
let { apiGatewayUrl: n } = e(), i = r();
|
|
218
232
|
return t({
|
|
219
233
|
mutationFn: async (e) => (await o(`mutation MybStartSession($input: MybStartSessionInput!) {
|
|
@@ -221,7 +235,7 @@ var a = () => {
|
|
|
221
235
|
}`, { input: e }, n)).mybStartSession,
|
|
222
236
|
onSuccess: () => i.invalidateQueries({ queryKey: s.sessions })
|
|
223
237
|
});
|
|
224
|
-
},
|
|
238
|
+
}, I = () => {
|
|
225
239
|
let { apiGatewayUrl: n } = e(), i = r();
|
|
226
240
|
return t({
|
|
227
241
|
mutationFn: async (e) => (await o(`mutation MybLogSet($input: MybLogSetInput!) {
|
|
@@ -241,7 +255,7 @@ var a = () => {
|
|
|
241
255
|
i.invalidateQueries({ queryKey: s.session(e.id) }), i.invalidateQueries({ queryKey: s.sessions });
|
|
242
256
|
}
|
|
243
257
|
});
|
|
244
|
-
},
|
|
258
|
+
}, L = (t = {}) => {
|
|
245
259
|
let { apiGatewayUrl: r } = e();
|
|
246
260
|
return n({
|
|
247
261
|
queryKey: [...s.goals, t],
|
|
@@ -256,7 +270,7 @@ var a = () => {
|
|
|
256
270
|
pagination: t.pagination ?? c
|
|
257
271
|
}, r)).mybListGoals
|
|
258
272
|
});
|
|
259
|
-
},
|
|
273
|
+
}, R = () => {
|
|
260
274
|
let { apiGatewayUrl: n } = e(), i = r();
|
|
261
275
|
return t({
|
|
262
276
|
mutationFn: async (e) => (await o(`mutation MybCreateGoal($input: MybCreateGoalInput!) {
|
|
@@ -264,7 +278,7 @@ var a = () => {
|
|
|
264
278
|
}`, { input: e }, n)).mybCreateGoal,
|
|
265
279
|
onSuccess: () => i.invalidateQueries({ queryKey: s.goals })
|
|
266
280
|
});
|
|
267
|
-
},
|
|
281
|
+
}, z = () => {
|
|
268
282
|
let { apiGatewayUrl: n } = e(), i = r();
|
|
269
283
|
return t({
|
|
270
284
|
mutationFn: async (e) => (await o(`mutation MybUpdateGoal($id: ID!, $input: MybUpdateGoalInput!) {
|
|
@@ -272,7 +286,7 @@ var a = () => {
|
|
|
272
286
|
}`, e, n)).mybUpdateGoal,
|
|
273
287
|
onSuccess: () => i.invalidateQueries({ queryKey: s.goals })
|
|
274
288
|
});
|
|
275
|
-
},
|
|
289
|
+
}, B = () => {
|
|
276
290
|
let { apiGatewayUrl: n } = e(), i = r();
|
|
277
291
|
return t({
|
|
278
292
|
mutationFn: async (e) => (await o(`mutation MybAchieveGoal($id: ID!) {
|
|
@@ -280,7 +294,7 @@ var a = () => {
|
|
|
280
294
|
}`, { id: e }, n)).mybAchieveGoal,
|
|
281
295
|
onSuccess: () => i.invalidateQueries({ queryKey: s.goals })
|
|
282
296
|
});
|
|
283
|
-
},
|
|
297
|
+
}, V = (t) => {
|
|
284
298
|
let { apiGatewayUrl: r } = e();
|
|
285
299
|
return n({
|
|
286
300
|
queryKey: [...s.activePlan, t ?? "me"],
|
|
@@ -288,7 +302,7 @@ var a = () => {
|
|
|
288
302
|
mybActiveNutritionPlan(memberId: $memberId) { ${g} }
|
|
289
303
|
}`, { memberId: t }, r)).mybActiveNutritionPlan
|
|
290
304
|
});
|
|
291
|
-
},
|
|
305
|
+
}, H = (t = {}) => {
|
|
292
306
|
let { apiGatewayUrl: r } = e();
|
|
293
307
|
return n({
|
|
294
308
|
queryKey: [...s.meals, t],
|
|
@@ -303,7 +317,7 @@ var a = () => {
|
|
|
303
317
|
pagination: t.pagination ?? c
|
|
304
318
|
}, r)).mybListMeals
|
|
305
319
|
});
|
|
306
|
-
},
|
|
320
|
+
}, U = (t, r) => {
|
|
307
321
|
let { apiGatewayUrl: i } = e();
|
|
308
322
|
return n({
|
|
309
323
|
queryKey: [
|
|
@@ -318,7 +332,7 @@ var a = () => {
|
|
|
318
332
|
memberId: r
|
|
319
333
|
}, i)).mybDailyMacros
|
|
320
334
|
});
|
|
321
|
-
},
|
|
335
|
+
}, W = () => {
|
|
322
336
|
let { apiGatewayUrl: n } = e(), i = r();
|
|
323
337
|
return t({
|
|
324
338
|
mutationFn: async (e) => (await o(`mutation MybCreateNutritionPlan($input: MybCreateNutritionPlanInput!) {
|
|
@@ -328,7 +342,7 @@ var a = () => {
|
|
|
328
342
|
i.invalidateQueries({ queryKey: s.activePlan });
|
|
329
343
|
}
|
|
330
344
|
});
|
|
331
|
-
},
|
|
345
|
+
}, G = () => {
|
|
332
346
|
let { apiGatewayUrl: n } = e(), i = r();
|
|
333
347
|
return t({
|
|
334
348
|
mutationFn: async (e) => (await o(`mutation MybUpdateNutritionPlan($id: ID!, $input: MybUpdateNutritionPlanInput!) {
|
|
@@ -338,7 +352,7 @@ var a = () => {
|
|
|
338
352
|
i.invalidateQueries({ queryKey: s.activePlan }), i.invalidateQueries({ queryKey: s.nutritionPlan(e.id) });
|
|
339
353
|
}
|
|
340
354
|
});
|
|
341
|
-
},
|
|
355
|
+
}, K = () => {
|
|
342
356
|
let { apiGatewayUrl: n } = e(), i = r();
|
|
343
357
|
return t({
|
|
344
358
|
mutationFn: async (e) => (await o(`mutation MybActivateNutritionPlan($id: ID!) {
|
|
@@ -348,7 +362,7 @@ var a = () => {
|
|
|
348
362
|
i.invalidateQueries({ queryKey: s.activePlan }), i.invalidateQueries({ queryKey: s.nutritionPlan(e.id) });
|
|
349
363
|
}
|
|
350
364
|
});
|
|
351
|
-
},
|
|
365
|
+
}, q = () => {
|
|
352
366
|
let { apiGatewayUrl: n } = e(), i = r();
|
|
353
367
|
return t({
|
|
354
368
|
mutationFn: async (e) => (await o(`mutation MybLogMeal($input: MybLogMealInput!) {
|
|
@@ -358,7 +372,7 @@ var a = () => {
|
|
|
358
372
|
i.invalidateQueries({ queryKey: s.meals }), i.invalidateQueries({ queryKey: s.dailyMacros }), e.planId && i.invalidateQueries({ queryKey: s.nutritionPlan(e.planId) });
|
|
359
373
|
}
|
|
360
374
|
});
|
|
361
|
-
},
|
|
375
|
+
}, J = () => {
|
|
362
376
|
let { apiGatewayUrl: n } = e(), i = r();
|
|
363
377
|
return t({
|
|
364
378
|
mutationFn: async (e) => (await o("mutation MybDeleteMeal($id: ID!) { mybDeleteMeal(id: $id) }", { id: e }, n)).mybDeleteMeal,
|
|
@@ -366,7 +380,7 @@ var a = () => {
|
|
|
366
380
|
i.invalidateQueries({ queryKey: s.meals }), i.invalidateQueries({ queryKey: s.dailyMacros });
|
|
367
381
|
}
|
|
368
382
|
});
|
|
369
|
-
},
|
|
383
|
+
}, Y = (t = {}) => {
|
|
370
384
|
let { apiGatewayUrl: r } = e();
|
|
371
385
|
return n({
|
|
372
386
|
queryKey: [...s.bodyMetrics, t],
|
|
@@ -381,7 +395,7 @@ var a = () => {
|
|
|
381
395
|
pagination: t.pagination ?? c
|
|
382
396
|
}, r)).mybListBodyMetrics
|
|
383
397
|
});
|
|
384
|
-
},
|
|
398
|
+
}, X = () => {
|
|
385
399
|
let { apiGatewayUrl: n } = e(), i = r();
|
|
386
400
|
return t({
|
|
387
401
|
mutationFn: async (e) => (await o(`mutation MybRecordBodyMetric($input: MybRecordMetricInput!) {
|
|
@@ -389,7 +403,7 @@ var a = () => {
|
|
|
389
403
|
}`, { input: e }, n)).mybRecordBodyMetric,
|
|
390
404
|
onSuccess: () => i.invalidateQueries({ queryKey: s.bodyMetrics })
|
|
391
405
|
});
|
|
392
|
-
},
|
|
406
|
+
}, Z = (t, r) => {
|
|
393
407
|
let { apiGatewayUrl: i } = e();
|
|
394
408
|
return n({
|
|
395
409
|
queryKey: [
|
|
@@ -407,7 +421,7 @@ var a = () => {
|
|
|
407
421
|
memberId: r
|
|
408
422
|
}, i)).mybListRecoveryLogs
|
|
409
423
|
});
|
|
410
|
-
},
|
|
424
|
+
}, Q = () => {
|
|
411
425
|
let { apiGatewayUrl: n } = e(), i = r();
|
|
412
426
|
return t({
|
|
413
427
|
mutationFn: async (e) => (await o(`mutation MybLogRecovery($input: MybLogRecoveryInput!) {
|
|
@@ -415,7 +429,7 @@ var a = () => {
|
|
|
415
429
|
}`, { input: e }, n)).mybLogRecovery,
|
|
416
430
|
onSuccess: () => i.invalidateQueries({ queryKey: s.recovery })
|
|
417
431
|
});
|
|
418
|
-
},
|
|
432
|
+
}, $ = (t, r) => {
|
|
419
433
|
let { apiGatewayUrl: i } = e();
|
|
420
434
|
return n({
|
|
421
435
|
queryKey: [
|
|
@@ -433,7 +447,7 @@ var a = () => {
|
|
|
433
447
|
memberId: r
|
|
434
448
|
}, i)).mybListCoaching
|
|
435
449
|
});
|
|
436
|
-
},
|
|
450
|
+
}, te = () => {
|
|
437
451
|
let { apiGatewayUrl: n } = e(), i = r();
|
|
438
452
|
return t({
|
|
439
453
|
mutationFn: async (e) => (await o(`mutation MybCreateCoaching($input: MybCreateCoachingInput!) {
|
|
@@ -441,7 +455,7 @@ var a = () => {
|
|
|
441
455
|
}`, { input: e }, n)).mybCreateCoaching,
|
|
442
456
|
onSuccess: () => i.invalidateQueries({ queryKey: s.coaching })
|
|
443
457
|
});
|
|
444
|
-
},
|
|
458
|
+
}, ne = () => {
|
|
445
459
|
let { apiGatewayUrl: n } = e(), i = r();
|
|
446
460
|
return t({
|
|
447
461
|
mutationFn: async (e) => (await o(`mutation MybTransitionCoaching($id: ID!, $status: MybCoachingStatus!) {
|
|
@@ -451,6 +465,6 @@ var a = () => {
|
|
|
451
465
|
});
|
|
452
466
|
};
|
|
453
467
|
//#endregion
|
|
454
|
-
export {
|
|
468
|
+
export { B as useMybAchieveGoal, K as useMybActivateNutritionPlan, V as useMybActiveNutritionPlan, j as useMybAddExercise, A as useMybAddWorkout, k as useMybArchiveProgram, Y as useMybBodyMetrics, $ as useMybCoaching, ee as useMybCompleteSession, te as useMybCreateCoaching, R as useMybCreateGoal, W as useMybCreateNutritionPlan, E as useMybCreateProgram, U as useMybDailyMacros, J as useMybDeleteMeal, P as useMybExerciseBestSet, L as useMybGoals, q as useMybLogMeal, Q as useMybLogRecovery, I as useMybLogSet, H as useMybMeals, C as useMybProgram, w as useMybProgramWorkouts, S as useMybPrograms, X as useMybRecordBodyMetric, Z as useMybRecoveryLogs, N as useMybSession, M as useMybSessions, F as useMybStartSession, ne as useMybTransitionCoaching, O as useMybTransitionProgram, z as useMybUpdateGoal, G as useMybUpdateNutritionPlan, D as useMybUpdateProgram, T as useMybWorkoutExercises };
|
|
455
469
|
|
|
456
470
|
//# sourceMappingURL=useMyBodyShieldApi.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useMyBodyShieldApi.js","names":[],"sources":["../../../src/mybodyshield/hooks/useMyBodyShieldApi.ts"],"sourcesContent":["/**\n * MyBodyShield API hooks\n * @module mybodyshield/hooks\n *\n * React-Query hooks wired DIRECTLY to the real `wspace-mybodyshield-svc`\n * federated GraphQL contract (the `myb*` operations). Every query/mutation\n * below uses an inline gql string whose fields match the svc\n * `.hive-schema.graphql` exactly. No mock data.\n *\n * Transport: `graphqlFetch({ gateway: 'workspace', workspaceToken: true })` —\n * the host shell supplies the gateway base URL + a workspace-scoped token.\n */\n\nimport { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';\nimport { graphqlFetch } from '@burdenoff/fe-libs/shared/graphql';\n\nimport { useMyBodyShield } from '../context';\nimport type {\n MybConnection,\n MybProgram,\n MybProgramStatus,\n MybWorkout,\n MybExercise,\n MybWorkoutSession,\n MybSessionStatus,\n MybSessionSet,\n MybGoal,\n MybGoalStatus,\n MybMealLog,\n MybMealType,\n MybNutritionPlan,\n MybDailyMacros,\n MybBodyMetric,\n MybBodyMetricType,\n MybRecoveryLog,\n MybRecoveryMetric,\n MybCoachingRelationship,\n MybCoachingStatus,\n MybCreateProgramInput,\n MybUpdateProgramInput,\n MybCreateWorkoutInput,\n MybCreateExerciseInput,\n MybStartSessionInput,\n MybLogSetInput,\n MybCompleteSessionInput,\n MybCreateGoalInput,\n MybUpdateGoalInput,\n MybCreateNutritionPlanInput,\n MybUpdateNutritionPlanInput,\n MybLogMealInput,\n MybRecordMetricInput,\n MybLogRecoveryInput,\n MybCreateCoachingInput,\n PaginationInput,\n} from '../types';\n\n// ============================================================================\n// Transport\n// ============================================================================\n\n/**\n * fe-libs `graphqlFetch` coalesces `query` responses in an in-memory cache keyed\n * by operationName+variables for ~15s (mutations are never cached). A read\n * issued right after a write (e.g. React-Query refetch triggered by our\n * `invalidateQueries`) would otherwise return the PRE-mutation response until the\n * TTL expires — making just-created/updated rows invisible until a full reload.\n * fe-libs' documented remedy is that write-performing callers clear those caches;\n * we do exactly that after every myb mutation so every follow-up read is fresh.\n */\ntype GraphqlReadCacheGlobals = {\n __graphqlFetchResponseCache?: { clear: () => void };\n __graphqlFetchPendingResponses?: { clear: () => void };\n __boffGraphQLResponseCache?: { clear: () => void };\n};\n\nconst clearGraphqlReadCaches = (): void => {\n const g = globalThis as GraphqlReadCacheGlobals;\n g.__graphqlFetchResponseCache?.clear();\n g.__graphqlFetchPendingResponses?.clear();\n g.__boffGraphQLResponseCache?.clear();\n};\n\nconst mybQuery = async <TData>(\n query: string,\n variables: Record<string, unknown>,\n baseUrl?: string\n): Promise<TData> => {\n const response = await graphqlFetch<TData>({\n gateway: 'workspace',\n baseUrl,\n query,\n variables,\n workspaceToken: true,\n });\n\n if (response.errors?.length) {\n throw new Error(response.errors[0]?.message ?? 'GraphQL request failed');\n }\n if (!response.data) {\n throw new Error('GraphQL response did not include data');\n }\n if (/^\\s*mutation\\b/.test(query)) {\n clearGraphqlReadCaches();\n }\n return response.data;\n};\n\n// React-Query key roots (one namespace per domain entity).\nconst KEY = {\n programs: ['mybodyshield', 'programs'] as const,\n program: (id: string) => ['mybodyshield', 'program', id] as const,\n programWorkouts: (id: string) => ['mybodyshield', 'program-workouts', id] as const,\n workoutExercises: (id: string) => ['mybodyshield', 'workout-exercises', id] as const,\n sessions: ['mybodyshield', 'sessions'] as const,\n session: (id: string) => ['mybodyshield', 'session', id] as const,\n exerciseBestSet: (name: string) => ['mybodyshield', 'exercise-best-set', name] as const,\n goals: ['mybodyshield', 'goals'] as const,\n goal: (id: string) => ['mybodyshield', 'goal', id] as const,\n meals: ['mybodyshield', 'meals'] as const,\n nutritionPlan: (id: string) => ['mybodyshield', 'nutrition-plan', id] as const,\n activePlan: ['mybodyshield', 'active-plan'] as const,\n dailyMacros: ['mybodyshield', 'daily-macros'] as const,\n bodyMetrics: ['mybodyshield', 'body-metrics'] as const,\n recovery: ['mybodyshield', 'recovery'] as const,\n coaching: ['mybodyshield', 'coaching'] as const,\n};\n\nconst DEFAULT_PAGE: PaginationInput = { limit: 50 };\n\n// ============================================================================\n// Reusable gql field selections (codegen-free strictness)\n// ============================================================================\n\nconst PROGRAM_FIELDS = `\n id workspaceId memberId coachId name description goalType level phase status\n weeks daysPerWeek startDate endDate tags createdAt updatedAt\n`;\n\nconst EXERCISE_FIELDS = `\n id workspaceId workoutId name instructions muscleGroup sets reps targetWeight\n restSeconds orderIndex demo { id } createdAt updatedAt\n`;\n\nconst WORKOUT_FIELDS = `\n id workspaceId programId name description type dayOfWeek weekNumber orderIndex\n durationMin createdAt updatedAt\n`;\n\nconst SET_FIELDS = `\n id workspaceId sessionId exerciseName setNumber reps weight distanceM durationSec\n isPersonalRecord createdAt updatedAt\n`;\n\nconst SESSION_FIELDS = `\n id workspaceId memberId programId workoutId status scheduledFor startedAt completedAt\n caloriesBurned perceivedExertion notes createdAt updatedAt\n`;\n\nconst GOAL_FIELDS = `\n id workspaceId memberId title description type status targetValue currentValue\n baselineValue unit targetDate achievedAt createdAt updatedAt\n`;\n\nconst MEAL_FIELDS = `\n id workspaceId memberId planId name mealType calories proteinG carbsG fatG servings\n isFavorite loggedAt createdAt updatedAt\n`;\n\nconst PLAN_FIELDS = `\n id workspaceId memberId coachId name goalType calorieTarget proteinTarget carbsTarget\n fatTarget startDate endDate isActive notes createdAt updatedAt\n`;\n\nconst METRIC_FIELDS = `\n id workspaceId memberId type value unit measuredAt notes photo { id } createdAt updatedAt\n`;\n\nconst RECOVERY_FIELDS = `\n id workspaceId memberId metric score sleepHours notes loggedAt createdAt updatedAt\n`;\n\nconst COACHING_FIELDS = `\n id workspaceId coachId memberId status specialty shareWorkouts shareNutrition\n shareMetrics startedAt endedAt notes createdAt updatedAt\n`;\n\nconst MACROS_FIELDS = `\n date calories proteinG carbsG fatG calorieTarget proteinTarget carbsTarget fatTarget\n`;\n\n/**\n * `MybWorkout.exercises` is a paginated `MybExerciseConnection` on the wire\n * (see `.hive-schema.graphql`); unwrap it back to the flat `MybExercise[]`\n * shape the rest of the app consumes.\n */\ntype MybWorkoutWithExerciseConnection = Omit<MybWorkout, 'exercises'> & {\n exercises: { items: MybExercise[] };\n};\n\nconst unwrapWorkoutItems = (items: MybWorkoutWithExerciseConnection[]): MybWorkout[] =>\n items.map(({ exercises, ...workout }) => ({ ...workout, exercises: exercises.items }));\n\n// ============================================================================\n// Programs (Training)\n// ============================================================================\n\nexport interface ProgramFilters {\n status?: MybProgramStatus;\n memberId?: string;\n pagination?: PaginationInput;\n}\n\nexport const useMybPrograms = (filters: ProgramFilters = {}) => {\n const { apiGatewayUrl } = useMyBodyShield();\n return useQuery({\n queryKey: [...KEY.programs, filters],\n queryFn: async () => {\n const result = await mybQuery<{ mybListPrograms: MybConnection<MybProgram> }>(\n `query MybListPrograms($status: MybProgramStatus, $memberId: ID, $pagination: PaginationInput) {\n mybListPrograms(status: $status, memberId: $memberId, pagination: $pagination) {\n items { ${PROGRAM_FIELDS} }\n pageInfo { cursor hasMore }\n }\n }`,\n {\n status: filters.status,\n memberId: filters.memberId,\n pagination: filters.pagination ?? DEFAULT_PAGE,\n },\n apiGatewayUrl\n );\n return result.mybListPrograms;\n },\n });\n};\n\nexport const useMybProgram = (id: string | undefined) => {\n const { apiGatewayUrl } = useMyBodyShield();\n return useQuery({\n queryKey: KEY.program(id ?? ''),\n enabled: Boolean(id),\n queryFn: async () => {\n const result = await mybQuery<{\n mybProgram:\n | (Omit<MybProgram, 'workouts'> & {\n workouts: { items: MybWorkoutWithExerciseConnection[] };\n })\n | null;\n }>(\n `query MybProgram($id: ID!) {\n mybProgram(id: $id) {\n ${PROGRAM_FIELDS}\n workouts {\n items {\n ${WORKOUT_FIELDS}\n exercises { items { ${EXERCISE_FIELDS} } }\n }\n }\n }\n }`,\n { id },\n apiGatewayUrl\n );\n if (!result.mybProgram) return null;\n const { workouts, ...program } = result.mybProgram;\n return { ...program, workouts: unwrapWorkoutItems(workouts.items) };\n },\n });\n};\n\nexport const useMybProgramWorkouts = (programId: string | undefined) => {\n const { apiGatewayUrl } = useMyBodyShield();\n return useQuery({\n queryKey: KEY.programWorkouts(programId ?? ''),\n enabled: Boolean(programId),\n queryFn: async () => {\n const result = await mybQuery<{\n mybProgramWorkouts: { items: MybWorkoutWithExerciseConnection[] };\n }>(\n `query MybProgramWorkouts($programId: ID!) {\n mybProgramWorkouts(programId: $programId) {\n items {\n ${WORKOUT_FIELDS}\n exercises { items { ${EXERCISE_FIELDS} } }\n }\n }\n }`,\n { programId },\n apiGatewayUrl\n );\n return unwrapWorkoutItems(result.mybProgramWorkouts.items);\n },\n });\n};\n\n/**\n * Planned exercises for a single workout, in `orderIndex` order — the\n * guided-workout stepper (BOFF-4096) walks a live session's `workoutId`\n * through these to drive the \"next exercise\" flow.\n */\nexport const useMybWorkoutExercises = (workoutId: string | undefined) => {\n const { apiGatewayUrl } = useMyBodyShield();\n return useQuery({\n queryKey: KEY.workoutExercises(workoutId ?? ''),\n enabled: Boolean(workoutId),\n queryFn: async () => {\n const result = await mybQuery<{ mybWorkoutExercises: { items: MybExercise[] } }>(\n `query MybWorkoutExercises($workoutId: ID!) {\n mybWorkoutExercises(workoutId: $workoutId, pagination: { limit: 100 }) {\n items { ${EXERCISE_FIELDS} }\n }\n }`,\n { workoutId },\n apiGatewayUrl\n );\n return result.mybWorkoutExercises.items.slice().sort((a, b) => a.orderIndex - b.orderIndex);\n },\n });\n};\n\nexport const useMybCreateProgram = () => {\n const { apiGatewayUrl } = useMyBodyShield();\n const qc = useQueryClient();\n return useMutation({\n mutationFn: async (input: MybCreateProgramInput) => {\n const result = await mybQuery<{ mybCreateProgram: MybProgram }>(\n `mutation MybCreateProgram($input: MybCreateProgramInput!) {\n mybCreateProgram(input: $input) { ${PROGRAM_FIELDS} }\n }`,\n { input },\n apiGatewayUrl\n );\n return result.mybCreateProgram;\n },\n onSuccess: () => qc.invalidateQueries({ queryKey: KEY.programs }),\n });\n};\n\nexport const useMybUpdateProgram = () => {\n const { apiGatewayUrl } = useMyBodyShield();\n const qc = useQueryClient();\n return useMutation({\n mutationFn: async (vars: { id: string; input: MybUpdateProgramInput }) => {\n const result = await mybQuery<{ mybUpdateProgram: MybProgram }>(\n `mutation MybUpdateProgram($id: ID!, $input: MybUpdateProgramInput!) {\n mybUpdateProgram(id: $id, input: $input) { ${PROGRAM_FIELDS} }\n }`,\n vars,\n apiGatewayUrl\n );\n return result.mybUpdateProgram;\n },\n onSuccess: (data) => {\n qc.invalidateQueries({ queryKey: KEY.programs });\n qc.invalidateQueries({ queryKey: KEY.program(data.id) });\n },\n });\n};\n\nexport const useMybTransitionProgram = () => {\n const { apiGatewayUrl } = useMyBodyShield();\n const qc = useQueryClient();\n return useMutation({\n mutationFn: async (vars: { id: string; status: MybProgramStatus }) => {\n const result = await mybQuery<{ mybTransitionProgram: MybProgram }>(\n `mutation MybTransitionProgram($id: ID!, $status: MybProgramStatus!) {\n mybTransitionProgram(id: $id, status: $status) { ${PROGRAM_FIELDS} }\n }`,\n vars,\n apiGatewayUrl\n );\n return result.mybTransitionProgram;\n },\n onSuccess: (data) => {\n qc.invalidateQueries({ queryKey: KEY.programs });\n qc.invalidateQueries({ queryKey: KEY.program(data.id) });\n },\n });\n};\n\nexport const useMybArchiveProgram = () => {\n const { apiGatewayUrl } = useMyBodyShield();\n const qc = useQueryClient();\n return useMutation({\n mutationFn: async (id: string) => {\n const result = await mybQuery<{ mybArchiveProgram: boolean }>(\n `mutation MybArchiveProgram($id: ID!) { mybArchiveProgram(id: $id) }`,\n { id },\n apiGatewayUrl\n );\n return result.mybArchiveProgram;\n },\n onSuccess: () => qc.invalidateQueries({ queryKey: KEY.programs }),\n });\n};\n\nexport const useMybAddWorkout = () => {\n const { apiGatewayUrl } = useMyBodyShield();\n const qc = useQueryClient();\n return useMutation({\n mutationFn: async (input: MybCreateWorkoutInput) => {\n const result = await mybQuery<{ mybAddWorkout: MybWorkout }>(\n `mutation MybAddWorkout($input: MybCreateWorkoutInput!) {\n mybAddWorkout(input: $input) { ${WORKOUT_FIELDS} }\n }`,\n { input },\n apiGatewayUrl\n );\n return result.mybAddWorkout;\n },\n onSuccess: (data) => {\n qc.invalidateQueries({ queryKey: KEY.program(data.programId) });\n qc.invalidateQueries({ queryKey: KEY.programWorkouts(data.programId) });\n },\n });\n};\n\nexport const useMybAddExercise = () => {\n const { apiGatewayUrl } = useMyBodyShield();\n const qc = useQueryClient();\n return useMutation({\n mutationFn: async (input: MybCreateExerciseInput) => {\n const result = await mybQuery<{ mybAddExercise: { id: string; workoutId: string } }>(\n `mutation MybAddExercise($input: MybCreateExerciseInput!) {\n mybAddExercise(input: $input) { id workoutId }\n }`,\n { input },\n apiGatewayUrl\n );\n return result.mybAddExercise;\n },\n onSuccess: () => {\n // Exercises live under a program's workouts; invalidate program + workout\n // caches so the newly-added exercise appears on the program detail tree.\n qc.invalidateQueries({ queryKey: ['mybodyshield', 'program'] });\n qc.invalidateQueries({ queryKey: ['mybodyshield', 'program-workouts'] });\n },\n });\n};\n\n// ============================================================================\n// Sessions / Sets (Training — live logging)\n// ============================================================================\n\nexport interface SessionFilters {\n status?: MybSessionStatus;\n programId?: string;\n pagination?: PaginationInput;\n}\n\nexport const useMybSessions = (filters: SessionFilters = {}) => {\n const { apiGatewayUrl } = useMyBodyShield();\n return useQuery({\n queryKey: [...KEY.sessions, filters],\n queryFn: async () => {\n const result = await mybQuery<{ mybListSessions: MybConnection<MybWorkoutSession> }>(\n `query MybListSessions($status: MybSessionStatus, $programId: ID, $pagination: PaginationInput) {\n mybListSessions(status: $status, programId: $programId, pagination: $pagination) {\n items { ${SESSION_FIELDS} }\n pageInfo { cursor hasMore }\n }\n }`,\n {\n status: filters.status,\n programId: filters.programId,\n pagination: filters.pagination ?? DEFAULT_PAGE,\n },\n apiGatewayUrl\n );\n return result.mybListSessions;\n },\n });\n};\n\nexport const useMybSession = (id: string | undefined) => {\n const { apiGatewayUrl } = useMyBodyShield();\n return useQuery({\n queryKey: KEY.session(id ?? ''),\n enabled: Boolean(id),\n queryFn: async () => {\n const result = await mybQuery<{\n mybSession: (Omit<MybWorkoutSession, 'sets'> & { sets: { items: MybSessionSet[] } }) | null;\n }>(\n `query MybSession($id: ID!) {\n mybSession(id: $id) {\n ${SESSION_FIELDS}\n sets { items { ${SET_FIELDS} } }\n }\n }`,\n { id },\n apiGatewayUrl\n );\n if (!result.mybSession) return null;\n const { sets, ...session } = result.mybSession;\n return { ...session, sets: sets.items };\n },\n });\n};\n\n/**\n * The calling member's own best-ever logged set for `exerciseName` (backend:\n * `mybExerciseBestSet`, self-scoped — no memberId argument). Drives live\n * auto-PR detection in the log-set dialog (BOFF-4096): `null` means the\n * member has never logged this exercise before, so there is no record to\n * beat yet.\n */\nexport const useMybExerciseBestSet = (exerciseName: string) => {\n const { apiGatewayUrl } = useMyBodyShield();\n const trimmed = exerciseName.trim();\n return useQuery({\n queryKey: KEY.exerciseBestSet(trimmed),\n enabled: trimmed.length > 0,\n queryFn: async () => {\n const result = await mybQuery<{ mybExerciseBestSet: MybSessionSet | null }>(\n `query MybExerciseBestSet($exerciseName: String!) {\n mybExerciseBestSet(exerciseName: $exerciseName) { ${SET_FIELDS} }\n }`,\n { exerciseName: trimmed },\n apiGatewayUrl\n );\n return result.mybExerciseBestSet;\n },\n // Best-set history changes rarely mid-session; a short stale window\n // avoids refiring on every keystroke while the exercise name settles.\n staleTime: 15_000,\n });\n};\n\nexport const useMybStartSession = () => {\n const { apiGatewayUrl } = useMyBodyShield();\n const qc = useQueryClient();\n return useMutation({\n mutationFn: async (input: MybStartSessionInput) => {\n const result = await mybQuery<{ mybStartSession: MybWorkoutSession }>(\n `mutation MybStartSession($input: MybStartSessionInput!) {\n mybStartSession(input: $input) { ${SESSION_FIELDS} }\n }`,\n { input },\n apiGatewayUrl\n );\n return result.mybStartSession;\n },\n onSuccess: () => qc.invalidateQueries({ queryKey: KEY.sessions }),\n });\n};\n\nexport const useMybLogSet = () => {\n const { apiGatewayUrl } = useMyBodyShield();\n const qc = useQueryClient();\n return useMutation({\n mutationFn: async (input: MybLogSetInput) => {\n const result = await mybQuery<{ mybLogSet: MybSessionSet }>(\n `mutation MybLogSet($input: MybLogSetInput!) {\n mybLogSet(input: $input) { ${SET_FIELDS} }\n }`,\n { input },\n apiGatewayUrl\n );\n return result.mybLogSet;\n },\n onSuccess: (data) => {\n qc.invalidateQueries({ queryKey: KEY.session(data.sessionId) });\n qc.invalidateQueries({ queryKey: KEY.sessions });\n // A just-logged set may itself now be the best-ever for this exercise —\n // refresh so the next auto-PR comparison (this session or a later one)\n // sees it (BOFF-4096).\n qc.invalidateQueries({ queryKey: KEY.exerciseBestSet(data.exerciseName) });\n },\n });\n};\n\nexport const useMybCompleteSession = () => {\n const { apiGatewayUrl } = useMyBodyShield();\n const qc = useQueryClient();\n return useMutation({\n mutationFn: async (vars: { id: string; input: MybCompleteSessionInput }) => {\n const result = await mybQuery<{ mybCompleteSession: MybWorkoutSession }>(\n `mutation MybCompleteSession($id: ID!, $input: MybCompleteSessionInput!) {\n mybCompleteSession(id: $id, input: $input) { ${SESSION_FIELDS} }\n }`,\n vars,\n apiGatewayUrl\n );\n return result.mybCompleteSession;\n },\n onSuccess: (data) => {\n qc.invalidateQueries({ queryKey: KEY.session(data.id) });\n qc.invalidateQueries({ queryKey: KEY.sessions });\n },\n });\n};\n\n// ============================================================================\n// Goals (Progress)\n// ============================================================================\n\nexport interface GoalFilters {\n status?: MybGoalStatus;\n memberId?: string;\n pagination?: PaginationInput;\n}\n\nexport const useMybGoals = (filters: GoalFilters = {}) => {\n const { apiGatewayUrl } = useMyBodyShield();\n return useQuery({\n queryKey: [...KEY.goals, filters],\n queryFn: async () => {\n const result = await mybQuery<{ mybListGoals: MybConnection<MybGoal> }>(\n `query MybListGoals($status: MybGoalStatus, $memberId: ID, $pagination: PaginationInput) {\n mybListGoals(status: $status, memberId: $memberId, pagination: $pagination) {\n items { ${GOAL_FIELDS} }\n pageInfo { cursor hasMore }\n }\n }`,\n {\n status: filters.status,\n memberId: filters.memberId,\n pagination: filters.pagination ?? DEFAULT_PAGE,\n },\n apiGatewayUrl\n );\n return result.mybListGoals;\n },\n });\n};\n\nexport const useMybCreateGoal = () => {\n const { apiGatewayUrl } = useMyBodyShield();\n const qc = useQueryClient();\n return useMutation({\n mutationFn: async (input: MybCreateGoalInput) => {\n const result = await mybQuery<{ mybCreateGoal: MybGoal }>(\n `mutation MybCreateGoal($input: MybCreateGoalInput!) {\n mybCreateGoal(input: $input) { ${GOAL_FIELDS} }\n }`,\n { input },\n apiGatewayUrl\n );\n return result.mybCreateGoal;\n },\n onSuccess: () => qc.invalidateQueries({ queryKey: KEY.goals }),\n });\n};\n\nexport const useMybUpdateGoal = () => {\n const { apiGatewayUrl } = useMyBodyShield();\n const qc = useQueryClient();\n return useMutation({\n mutationFn: async (vars: { id: string; input: MybUpdateGoalInput }) => {\n const result = await mybQuery<{ mybUpdateGoal: MybGoal }>(\n `mutation MybUpdateGoal($id: ID!, $input: MybUpdateGoalInput!) {\n mybUpdateGoal(id: $id, input: $input) { ${GOAL_FIELDS} }\n }`,\n vars,\n apiGatewayUrl\n );\n return result.mybUpdateGoal;\n },\n onSuccess: () => qc.invalidateQueries({ queryKey: KEY.goals }),\n });\n};\n\nexport const useMybAchieveGoal = () => {\n const { apiGatewayUrl } = useMyBodyShield();\n const qc = useQueryClient();\n return useMutation({\n mutationFn: async (id: string) => {\n const result = await mybQuery<{ mybAchieveGoal: MybGoal }>(\n `mutation MybAchieveGoal($id: ID!) {\n mybAchieveGoal(id: $id) { ${GOAL_FIELDS} }\n }`,\n { id },\n apiGatewayUrl\n );\n return result.mybAchieveGoal;\n },\n onSuccess: () => qc.invalidateQueries({ queryKey: KEY.goals }),\n });\n};\n\n// ============================================================================\n// Nutrition (plans, meals, macros)\n// ============================================================================\n\nexport const useMybActiveNutritionPlan = (memberId?: string) => {\n const { apiGatewayUrl } = useMyBodyShield();\n return useQuery({\n queryKey: [...KEY.activePlan, memberId ?? 'me'],\n queryFn: async () => {\n const result = await mybQuery<{ mybActiveNutritionPlan: MybNutritionPlan | null }>(\n `query MybActiveNutritionPlan($memberId: ID) {\n mybActiveNutritionPlan(memberId: $memberId) { ${PLAN_FIELDS} }\n }`,\n { memberId },\n apiGatewayUrl\n );\n return result.mybActiveNutritionPlan;\n },\n });\n};\n\nexport const useMybNutritionPlan = (id: string | undefined) => {\n const { apiGatewayUrl } = useMyBodyShield();\n return useQuery({\n queryKey: KEY.nutritionPlan(id ?? ''),\n enabled: Boolean(id),\n queryFn: async () => {\n const result = await mybQuery<{ mybNutritionPlan: MybNutritionPlan | null }>(\n `query MybNutritionPlan($id: ID!) {\n mybNutritionPlan(id: $id) {\n ${PLAN_FIELDS}\n meals { ${MEAL_FIELDS} }\n }\n }`,\n { id },\n apiGatewayUrl\n );\n return result.mybNutritionPlan;\n },\n });\n};\n\nexport interface MealFilters {\n mealType?: MybMealType;\n planId?: string;\n pagination?: PaginationInput;\n}\n\nexport const useMybMeals = (filters: MealFilters = {}) => {\n const { apiGatewayUrl } = useMyBodyShield();\n return useQuery({\n queryKey: [...KEY.meals, filters],\n queryFn: async () => {\n const result = await mybQuery<{ mybListMeals: MybConnection<MybMealLog> }>(\n `query MybListMeals($mealType: MybMealType, $planId: ID, $pagination: PaginationInput) {\n mybListMeals(mealType: $mealType, planId: $planId, pagination: $pagination) {\n items { ${MEAL_FIELDS} }\n pageInfo { cursor hasMore }\n }\n }`,\n {\n mealType: filters.mealType,\n planId: filters.planId,\n pagination: filters.pagination ?? DEFAULT_PAGE,\n },\n apiGatewayUrl\n );\n return result.mybListMeals;\n },\n });\n};\n\nexport const useMybDailyMacros = (date?: string, memberId?: string) => {\n const { apiGatewayUrl } = useMyBodyShield();\n return useQuery({\n queryKey: [...KEY.dailyMacros, date ?? 'today', memberId ?? 'me'],\n queryFn: async () => {\n const result = await mybQuery<{ mybDailyMacros: MybDailyMacros }>(\n `query MybDailyMacros($date: DateTime, $memberId: ID) {\n mybDailyMacros(date: $date, memberId: $memberId) { ${MACROS_FIELDS} }\n }`,\n { date, memberId },\n apiGatewayUrl\n );\n return result.mybDailyMacros;\n },\n });\n};\n\nexport const useMybCreateNutritionPlan = () => {\n const { apiGatewayUrl } = useMyBodyShield();\n const qc = useQueryClient();\n return useMutation({\n mutationFn: async (input: MybCreateNutritionPlanInput) => {\n const result = await mybQuery<{ mybCreateNutritionPlan: MybNutritionPlan }>(\n `mutation MybCreateNutritionPlan($input: MybCreateNutritionPlanInput!) {\n mybCreateNutritionPlan(input: $input) { ${PLAN_FIELDS} }\n }`,\n { input },\n apiGatewayUrl\n );\n return result.mybCreateNutritionPlan;\n },\n onSuccess: () => {\n qc.invalidateQueries({ queryKey: KEY.activePlan });\n },\n });\n};\n\nexport const useMybUpdateNutritionPlan = () => {\n const { apiGatewayUrl } = useMyBodyShield();\n const qc = useQueryClient();\n return useMutation({\n mutationFn: async (vars: { id: string; input: MybUpdateNutritionPlanInput }) => {\n const result = await mybQuery<{ mybUpdateNutritionPlan: MybNutritionPlan }>(\n `mutation MybUpdateNutritionPlan($id: ID!, $input: MybUpdateNutritionPlanInput!) {\n mybUpdateNutritionPlan(id: $id, input: $input) { ${PLAN_FIELDS} }\n }`,\n vars,\n apiGatewayUrl\n );\n return result.mybUpdateNutritionPlan;\n },\n onSuccess: (data) => {\n qc.invalidateQueries({ queryKey: KEY.activePlan });\n qc.invalidateQueries({ queryKey: KEY.nutritionPlan(data.id) });\n },\n });\n};\n\nexport const useMybActivateNutritionPlan = () => {\n const { apiGatewayUrl } = useMyBodyShield();\n const qc = useQueryClient();\n return useMutation({\n mutationFn: async (id: string) => {\n const result = await mybQuery<{ mybActivateNutritionPlan: MybNutritionPlan }>(\n `mutation MybActivateNutritionPlan($id: ID!) {\n mybActivateNutritionPlan(id: $id) { ${PLAN_FIELDS} }\n }`,\n { id },\n apiGatewayUrl\n );\n return result.mybActivateNutritionPlan;\n },\n onSuccess: (data) => {\n qc.invalidateQueries({ queryKey: KEY.activePlan });\n qc.invalidateQueries({ queryKey: KEY.nutritionPlan(data.id) });\n },\n });\n};\n\nexport const useMybLogMeal = () => {\n const { apiGatewayUrl } = useMyBodyShield();\n const qc = useQueryClient();\n return useMutation({\n mutationFn: async (input: MybLogMealInput) => {\n const result = await mybQuery<{ mybLogMeal: MybMealLog }>(\n `mutation MybLogMeal($input: MybLogMealInput!) {\n mybLogMeal(input: $input) { ${MEAL_FIELDS} }\n }`,\n { input },\n apiGatewayUrl\n );\n return result.mybLogMeal;\n },\n onSuccess: (data) => {\n qc.invalidateQueries({ queryKey: KEY.meals });\n qc.invalidateQueries({ queryKey: KEY.dailyMacros });\n if (data.planId) qc.invalidateQueries({ queryKey: KEY.nutritionPlan(data.planId) });\n },\n });\n};\n\nexport const useMybDeleteMeal = () => {\n const { apiGatewayUrl } = useMyBodyShield();\n const qc = useQueryClient();\n return useMutation({\n mutationFn: async (id: string) => {\n const result = await mybQuery<{ mybDeleteMeal: boolean }>(\n `mutation MybDeleteMeal($id: ID!) { mybDeleteMeal(id: $id) }`,\n { id },\n apiGatewayUrl\n );\n return result.mybDeleteMeal;\n },\n onSuccess: () => {\n qc.invalidateQueries({ queryKey: KEY.meals });\n qc.invalidateQueries({ queryKey: KEY.dailyMacros });\n },\n });\n};\n\n// ============================================================================\n// Body metrics (Progress)\n// ============================================================================\n\nexport interface BodyMetricFilters {\n type?: MybBodyMetricType;\n memberId?: string;\n pagination?: PaginationInput;\n}\n\nexport const useMybBodyMetrics = (filters: BodyMetricFilters = {}) => {\n const { apiGatewayUrl } = useMyBodyShield();\n return useQuery({\n queryKey: [...KEY.bodyMetrics, filters],\n queryFn: async () => {\n const result = await mybQuery<{ mybListBodyMetrics: MybConnection<MybBodyMetric> }>(\n `query MybListBodyMetrics($type: MybBodyMetricType, $memberId: ID, $pagination: PaginationInput) {\n mybListBodyMetrics(type: $type, memberId: $memberId, pagination: $pagination) {\n items { ${METRIC_FIELDS} }\n pageInfo { cursor hasMore }\n }\n }`,\n {\n type: filters.type,\n memberId: filters.memberId,\n pagination: filters.pagination ?? DEFAULT_PAGE,\n },\n apiGatewayUrl\n );\n return result.mybListBodyMetrics;\n },\n });\n};\n\nexport const useMybRecordBodyMetric = () => {\n const { apiGatewayUrl } = useMyBodyShield();\n const qc = useQueryClient();\n return useMutation({\n mutationFn: async (input: MybRecordMetricInput) => {\n const result = await mybQuery<{ mybRecordBodyMetric: MybBodyMetric }>(\n `mutation MybRecordBodyMetric($input: MybRecordMetricInput!) {\n mybRecordBodyMetric(input: $input) { ${METRIC_FIELDS} }\n }`,\n { input },\n apiGatewayUrl\n );\n return result.mybRecordBodyMetric;\n },\n onSuccess: () => qc.invalidateQueries({ queryKey: KEY.bodyMetrics }),\n });\n};\n\n// ============================================================================\n// Recovery (Wellbeing)\n// ============================================================================\n\nexport const useMybRecoveryLogs = (metric?: MybRecoveryMetric, memberId?: string) => {\n const { apiGatewayUrl } = useMyBodyShield();\n return useQuery({\n queryKey: [...KEY.recovery, metric ?? 'all', memberId ?? 'me'],\n queryFn: async () => {\n const result = await mybQuery<{ mybListRecoveryLogs: MybConnection<MybRecoveryLog> }>(\n `query MybListRecoveryLogs($metric: MybRecoveryMetric, $memberId: ID) {\n mybListRecoveryLogs(metric: $metric, memberId: $memberId) {\n items { ${RECOVERY_FIELDS} }\n pageInfo { cursor hasMore }\n }\n }`,\n { metric, memberId },\n apiGatewayUrl\n );\n return result.mybListRecoveryLogs;\n },\n });\n};\n\nexport const useMybLogRecovery = () => {\n const { apiGatewayUrl } = useMyBodyShield();\n const qc = useQueryClient();\n return useMutation({\n mutationFn: async (input: MybLogRecoveryInput) => {\n const result = await mybQuery<{ mybLogRecovery: MybRecoveryLog }>(\n `mutation MybLogRecovery($input: MybLogRecoveryInput!) {\n mybLogRecovery(input: $input) { ${RECOVERY_FIELDS} }\n }`,\n { input },\n apiGatewayUrl\n );\n return result.mybLogRecovery;\n },\n onSuccess: () => qc.invalidateQueries({ queryKey: KEY.recovery }),\n });\n};\n\n// ============================================================================\n// Coaching (Wellbeing)\n// ============================================================================\n\nexport const useMybCoaching = (status?: MybCoachingStatus, memberId?: string) => {\n const { apiGatewayUrl } = useMyBodyShield();\n return useQuery({\n queryKey: [...KEY.coaching, status ?? 'all', memberId ?? 'me'],\n queryFn: async () => {\n const result = await mybQuery<{ mybListCoaching: MybConnection<MybCoachingRelationship> }>(\n `query MybListCoaching($status: MybCoachingStatus, $memberId: ID) {\n mybListCoaching(status: $status, memberId: $memberId) {\n items { ${COACHING_FIELDS} }\n pageInfo { cursor hasMore }\n }\n }`,\n { status, memberId },\n apiGatewayUrl\n );\n return result.mybListCoaching;\n },\n });\n};\n\nexport const useMybCreateCoaching = () => {\n const { apiGatewayUrl } = useMyBodyShield();\n const qc = useQueryClient();\n return useMutation({\n mutationFn: async (input: MybCreateCoachingInput) => {\n const result = await mybQuery<{ mybCreateCoaching: MybCoachingRelationship }>(\n `mutation MybCreateCoaching($input: MybCreateCoachingInput!) {\n mybCreateCoaching(input: $input) { ${COACHING_FIELDS} }\n }`,\n { input },\n apiGatewayUrl\n );\n return result.mybCreateCoaching;\n },\n onSuccess: () => qc.invalidateQueries({ queryKey: KEY.coaching }),\n });\n};\n\nexport const useMybTransitionCoaching = () => {\n const { apiGatewayUrl } = useMyBodyShield();\n const qc = useQueryClient();\n return useMutation({\n mutationFn: async (vars: { id: string; status: MybCoachingStatus }) => {\n const result = await mybQuery<{ mybTransitionCoaching: MybCoachingRelationship }>(\n `mutation MybTransitionCoaching($id: ID!, $status: MybCoachingStatus!) {\n mybTransitionCoaching(id: $id, status: $status) { ${COACHING_FIELDS} }\n }`,\n vars,\n apiGatewayUrl\n );\n return result.mybTransitionCoaching;\n },\n onSuccess: () => qc.invalidateQueries({ queryKey: KEY.coaching }),\n });\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"useMyBodyShieldApi.js","names":[],"sources":["../../../src/mybodyshield/hooks/useMyBodyShieldApi.ts"],"sourcesContent":["/**\n * MyBodyShield API hooks\n * @module mybodyshield/hooks\n *\n * React-Query hooks wired DIRECTLY to the real `wspace-mybodyshield-svc`\n * federated GraphQL contract (the `myb*` operations). Every query/mutation\n * below uses an inline gql string whose fields match the svc\n * `.hive-schema.graphql` exactly. No mock data.\n *\n * Transport: `graphqlFetch({ gateway: 'workspace', workspaceToken: true })` —\n * the host shell supplies the gateway base URL + a workspace-scoped token.\n */\n\nimport { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';\nimport { graphqlFetch } from '@burdenoff/fe-libs/shared/graphql';\n\nimport { useMyBodyShield } from '../context';\nimport type {\n MybConnection,\n MybProgram,\n MybProgramStatus,\n MybWorkout,\n MybExercise,\n MybWorkoutSession,\n MybSessionStatus,\n MybSessionSet,\n MybGoal,\n MybGoalStatus,\n MybMealLog,\n MybMealType,\n MybNutritionPlan,\n MybDailyMacros,\n MybBodyMetric,\n MybBodyMetricType,\n MybRecoveryLog,\n MybRecoveryMetric,\n MybCoachingRelationship,\n MybCoachingStatus,\n MybCreateProgramInput,\n MybUpdateProgramInput,\n MybCreateWorkoutInput,\n MybCreateExerciseInput,\n MybStartSessionInput,\n MybLogSetInput,\n MybCompleteSessionInput,\n MybCreateGoalInput,\n MybUpdateGoalInput,\n MybCreateNutritionPlanInput,\n MybUpdateNutritionPlanInput,\n MybLogMealInput,\n MybRecordMetricInput,\n MybLogRecoveryInput,\n MybCreateCoachingInput,\n PaginationInput,\n} from '../types';\n\n// ============================================================================\n// Transport\n// ============================================================================\n\n/**\n * fe-libs `graphqlFetch` coalesces `query` responses in an in-memory cache keyed\n * by operationName+variables for ~15s (mutations are never cached). A read\n * issued right after a write (e.g. React-Query refetch triggered by our\n * `invalidateQueries`) would otherwise return the PRE-mutation response until the\n * TTL expires — making just-created/updated rows invisible until a full reload.\n * fe-libs' documented remedy is that write-performing callers clear those caches;\n * we do exactly that after every myb mutation so every follow-up read is fresh.\n */\ntype GraphqlReadCacheGlobals = {\n __graphqlFetchResponseCache?: { clear: () => void };\n __graphqlFetchPendingResponses?: { clear: () => void };\n __boffGraphQLResponseCache?: { clear: () => void };\n};\n\nconst clearGraphqlReadCaches = (): void => {\n const g = globalThis as GraphqlReadCacheGlobals;\n g.__graphqlFetchResponseCache?.clear();\n g.__graphqlFetchPendingResponses?.clear();\n g.__boffGraphQLResponseCache?.clear();\n};\n\nconst mybQuery = async <TData>(\n query: string,\n variables: Record<string, unknown>,\n baseUrl?: string\n): Promise<TData> => {\n const response = await graphqlFetch<TData>({\n gateway: 'workspace',\n baseUrl,\n query,\n variables,\n workspaceToken: true,\n });\n\n if (response.errors?.length) {\n throw new Error(response.errors[0]?.message ?? 'GraphQL request failed');\n }\n if (!response.data) {\n throw new Error('GraphQL response did not include data');\n }\n if (/^\\s*mutation\\b/.test(query)) {\n clearGraphqlReadCaches();\n }\n return response.data;\n};\n\n// React-Query key roots (one namespace per domain entity).\nconst KEY = {\n programs: ['mybodyshield', 'programs'] as const,\n program: (id: string) => ['mybodyshield', 'program', id] as const,\n programWorkouts: (id: string) => ['mybodyshield', 'program-workouts', id] as const,\n workoutExercises: (id: string) => ['mybodyshield', 'workout-exercises', id] as const,\n sessions: ['mybodyshield', 'sessions'] as const,\n session: (id: string) => ['mybodyshield', 'session', id] as const,\n exerciseBestSet: (name: string) => ['mybodyshield', 'exercise-best-set', name] as const,\n goals: ['mybodyshield', 'goals'] as const,\n goal: (id: string) => ['mybodyshield', 'goal', id] as const,\n meals: ['mybodyshield', 'meals'] as const,\n nutritionPlan: (id: string) => ['mybodyshield', 'nutrition-plan', id] as const,\n activePlan: ['mybodyshield', 'active-plan'] as const,\n dailyMacros: ['mybodyshield', 'daily-macros'] as const,\n bodyMetrics: ['mybodyshield', 'body-metrics'] as const,\n recovery: ['mybodyshield', 'recovery'] as const,\n coaching: ['mybodyshield', 'coaching'] as const,\n};\n\nconst DEFAULT_PAGE: PaginationInput = { limit: 50 };\n\n// ============================================================================\n// Reusable gql field selections (codegen-free strictness)\n// ============================================================================\n\nconst PROGRAM_FIELDS = `\n id workspaceId memberId coachId name description goalType level phase status\n weeks daysPerWeek startDate endDate tags createdAt updatedAt\n`;\n\nconst EXERCISE_FIELDS = `\n id workspaceId workoutId name instructions muscleGroup sets reps targetWeight\n restSeconds orderIndex demo { id } createdAt updatedAt\n`;\n\nconst WORKOUT_FIELDS = `\n id workspaceId programId name description type dayOfWeek weekNumber orderIndex\n durationMin createdAt updatedAt\n`;\n\nconst SET_FIELDS = `\n id workspaceId sessionId exerciseName setNumber reps weight distanceM durationSec\n isPersonalRecord createdAt updatedAt\n`;\n\nconst SESSION_FIELDS = `\n id workspaceId memberId programId workoutId status scheduledFor startedAt completedAt\n caloriesBurned perceivedExertion notes createdAt updatedAt\n`;\n\nconst GOAL_FIELDS = `\n id workspaceId memberId title description type status targetValue currentValue\n baselineValue unit targetDate achievedAt createdAt updatedAt\n`;\n\nconst MEAL_FIELDS = `\n id workspaceId memberId planId name mealType calories proteinG carbsG fatG servings\n isFavorite loggedAt createdAt updatedAt\n`;\n\nconst PLAN_FIELDS = `\n id workspaceId memberId coachId name goalType calorieTarget proteinTarget carbsTarget\n fatTarget startDate endDate isActive notes createdAt updatedAt\n`;\n\nconst METRIC_FIELDS = `\n id workspaceId memberId type value unit measuredAt notes photo { id } createdAt updatedAt\n`;\n\nconst RECOVERY_FIELDS = `\n id workspaceId memberId metric score sleepHours notes loggedAt createdAt updatedAt\n`;\n\nconst COACHING_FIELDS = `\n id workspaceId coachId memberId status specialty shareWorkouts shareNutrition\n shareMetrics startedAt endedAt notes createdAt updatedAt\n`;\n\nconst MACROS_FIELDS = `\n date calories proteinG carbsG fatG calorieTarget proteinTarget carbsTarget fatTarget\n`;\n\n/**\n * `MybWorkout.exercises` is a paginated `MybExerciseConnection` on the wire\n * (see `.hive-schema.graphql`); unwrap it back to the flat `MybExercise[]`\n * shape the rest of the app consumes.\n */\ntype MybWorkoutWithExerciseConnection = Omit<MybWorkout, 'exercises'> & {\n exercises: { items: MybExercise[] };\n};\n\nconst unwrapWorkoutItems = (items: MybWorkoutWithExerciseConnection[]): MybWorkout[] =>\n items.map(({ exercises, ...workout }) => ({ ...workout, exercises: exercises.items }));\n\n// ============================================================================\n// Programs (Training)\n// ============================================================================\n\nexport interface ProgramFilters {\n status?: MybProgramStatus;\n memberId?: string;\n pagination?: PaginationInput;\n}\n\nexport const useMybPrograms = (filters: ProgramFilters = {}) => {\n const { apiGatewayUrl } = useMyBodyShield();\n return useQuery({\n queryKey: [...KEY.programs, filters],\n queryFn: async () => {\n const result = await mybQuery<{ mybListPrograms: MybConnection<MybProgram> }>(\n `query MybListPrograms($status: MybProgramStatus, $memberId: ID, $pagination: PaginationInput) {\n mybListPrograms(status: $status, memberId: $memberId, pagination: $pagination) {\n items { ${PROGRAM_FIELDS} }\n pageInfo { cursor hasMore }\n }\n }`,\n {\n status: filters.status,\n memberId: filters.memberId,\n pagination: filters.pagination ?? DEFAULT_PAGE,\n },\n apiGatewayUrl\n );\n return result.mybListPrograms;\n },\n });\n};\n\nexport const useMybProgram = (id: string | undefined) => {\n const { apiGatewayUrl } = useMyBodyShield();\n return useQuery({\n queryKey: KEY.program(id ?? ''),\n enabled: Boolean(id),\n queryFn: async () => {\n const result = await mybQuery<{\n mybProgram:\n | (Omit<MybProgram, 'workouts'> & {\n workouts: { items: MybWorkoutWithExerciseConnection[] };\n })\n | null;\n }>(\n `query MybProgram($id: ID!) {\n mybProgram(id: $id) {\n ${PROGRAM_FIELDS}\n workouts {\n items {\n ${WORKOUT_FIELDS}\n exercises { items { ${EXERCISE_FIELDS} } }\n }\n }\n }\n }`,\n { id },\n apiGatewayUrl\n );\n if (!result.mybProgram) return null;\n const { workouts, ...program } = result.mybProgram;\n return { ...program, workouts: unwrapWorkoutItems(workouts.items) };\n },\n });\n};\n\nexport const useMybProgramWorkouts = (programId: string | undefined) => {\n const { apiGatewayUrl } = useMyBodyShield();\n return useQuery({\n queryKey: KEY.programWorkouts(programId ?? ''),\n enabled: Boolean(programId),\n queryFn: async () => {\n const result = await mybQuery<{\n mybProgramWorkouts: { items: MybWorkoutWithExerciseConnection[] };\n }>(\n `query MybProgramWorkouts($programId: ID!) {\n mybProgramWorkouts(programId: $programId) {\n items {\n ${WORKOUT_FIELDS}\n exercises { items { ${EXERCISE_FIELDS} } }\n }\n }\n }`,\n { programId },\n apiGatewayUrl\n );\n return unwrapWorkoutItems(result.mybProgramWorkouts.items);\n },\n });\n};\n\n/**\n * Planned exercises for a single workout, in `orderIndex` order — the\n * guided-workout stepper (BOFF-4096) walks a live session's `workoutId`\n * through these to drive the \"next exercise\" flow.\n */\nexport const useMybWorkoutExercises = (workoutId: string | undefined) => {\n const { apiGatewayUrl } = useMyBodyShield();\n return useQuery({\n queryKey: KEY.workoutExercises(workoutId ?? ''),\n enabled: Boolean(workoutId),\n queryFn: async () => {\n const result = await mybQuery<{ mybWorkoutExercises: { items: MybExercise[] } }>(\n `query MybWorkoutExercises($workoutId: ID!) {\n mybWorkoutExercises(workoutId: $workoutId, pagination: { limit: 100 }) {\n items { ${EXERCISE_FIELDS} }\n }\n }`,\n { workoutId },\n apiGatewayUrl\n );\n return result.mybWorkoutExercises.items.slice().sort((a, b) => a.orderIndex - b.orderIndex);\n },\n });\n};\n\nexport const useMybCreateProgram = () => {\n const { apiGatewayUrl } = useMyBodyShield();\n const qc = useQueryClient();\n return useMutation({\n mutationFn: async (input: MybCreateProgramInput) => {\n const result = await mybQuery<{ mybCreateProgram: MybProgram }>(\n `mutation MybCreateProgram($input: MybCreateProgramInput!) {\n mybCreateProgram(input: $input) { ${PROGRAM_FIELDS} }\n }`,\n { input },\n apiGatewayUrl\n );\n return result.mybCreateProgram;\n },\n onSuccess: () => qc.invalidateQueries({ queryKey: KEY.programs }),\n });\n};\n\nexport const useMybUpdateProgram = () => {\n const { apiGatewayUrl } = useMyBodyShield();\n const qc = useQueryClient();\n return useMutation({\n mutationFn: async (vars: { id: string; input: MybUpdateProgramInput }) => {\n const result = await mybQuery<{ mybUpdateProgram: MybProgram }>(\n `mutation MybUpdateProgram($id: ID!, $input: MybUpdateProgramInput!) {\n mybUpdateProgram(id: $id, input: $input) { ${PROGRAM_FIELDS} }\n }`,\n vars,\n apiGatewayUrl\n );\n return result.mybUpdateProgram;\n },\n onSuccess: (data) => {\n qc.invalidateQueries({ queryKey: KEY.programs });\n qc.invalidateQueries({ queryKey: KEY.program(data.id) });\n },\n });\n};\n\nexport const useMybTransitionProgram = () => {\n const { apiGatewayUrl } = useMyBodyShield();\n const qc = useQueryClient();\n return useMutation({\n mutationFn: async (vars: { id: string; status: MybProgramStatus }) => {\n const result = await mybQuery<{ mybTransitionProgram: MybProgram }>(\n `mutation MybTransitionProgram($id: ID!, $status: MybProgramStatus!) {\n mybTransitionProgram(id: $id, status: $status) { ${PROGRAM_FIELDS} }\n }`,\n vars,\n apiGatewayUrl\n );\n return result.mybTransitionProgram;\n },\n onSuccess: (data) => {\n qc.invalidateQueries({ queryKey: KEY.programs });\n qc.invalidateQueries({ queryKey: KEY.program(data.id) });\n },\n });\n};\n\nexport const useMybArchiveProgram = () => {\n const { apiGatewayUrl } = useMyBodyShield();\n const qc = useQueryClient();\n return useMutation({\n mutationFn: async (id: string) => {\n const result = await mybQuery<{ mybArchiveProgram: boolean }>(\n `mutation MybArchiveProgram($id: ID!) { mybArchiveProgram(id: $id) }`,\n { id },\n apiGatewayUrl\n );\n return result.mybArchiveProgram;\n },\n onSuccess: () => qc.invalidateQueries({ queryKey: KEY.programs }),\n });\n};\n\nexport const useMybAddWorkout = () => {\n const { apiGatewayUrl } = useMyBodyShield();\n const qc = useQueryClient();\n return useMutation({\n mutationFn: async (input: MybCreateWorkoutInput) => {\n const result = await mybQuery<{ mybAddWorkout: MybWorkout }>(\n `mutation MybAddWorkout($input: MybCreateWorkoutInput!) {\n mybAddWorkout(input: $input) { ${WORKOUT_FIELDS} }\n }`,\n { input },\n apiGatewayUrl\n );\n return result.mybAddWorkout;\n },\n onSuccess: (data) => {\n qc.invalidateQueries({ queryKey: KEY.program(data.programId) });\n qc.invalidateQueries({ queryKey: KEY.programWorkouts(data.programId) });\n },\n });\n};\n\nexport const useMybAddExercise = () => {\n const { apiGatewayUrl } = useMyBodyShield();\n const qc = useQueryClient();\n return useMutation({\n mutationFn: async (input: MybCreateExerciseInput) => {\n const result = await mybQuery<{ mybAddExercise: { id: string; workoutId: string } }>(\n `mutation MybAddExercise($input: MybCreateExerciseInput!) {\n mybAddExercise(input: $input) { id workoutId }\n }`,\n { input },\n apiGatewayUrl\n );\n return result.mybAddExercise;\n },\n onSuccess: () => {\n // Exercises live under a program's workouts; invalidate program + workout\n // caches so the newly-added exercise appears on the program detail tree.\n qc.invalidateQueries({ queryKey: ['mybodyshield', 'program'] });\n qc.invalidateQueries({ queryKey: ['mybodyshield', 'program-workouts'] });\n },\n });\n};\n\n// ============================================================================\n// Sessions / Sets (Training — live logging)\n// ============================================================================\n\nexport interface SessionFilters {\n status?: MybSessionStatus;\n programId?: string;\n pagination?: PaginationInput;\n}\n\nexport const useMybSessions = (filters: SessionFilters = {}) => {\n const { apiGatewayUrl } = useMyBodyShield();\n return useQuery({\n queryKey: [...KEY.sessions, filters],\n queryFn: async () => {\n const result = await mybQuery<{ mybListSessions: MybConnection<MybWorkoutSession> }>(\n `query MybListSessions($status: MybSessionStatus, $programId: ID, $pagination: PaginationInput) {\n mybListSessions(status: $status, programId: $programId, pagination: $pagination) {\n items { ${SESSION_FIELDS} }\n pageInfo { cursor hasMore }\n }\n }`,\n {\n status: filters.status,\n programId: filters.programId,\n pagination: filters.pagination ?? DEFAULT_PAGE,\n },\n apiGatewayUrl\n );\n return result.mybListSessions;\n },\n });\n};\n\nexport const useMybSession = (id: string | undefined) => {\n const { apiGatewayUrl } = useMyBodyShield();\n return useQuery({\n queryKey: KEY.session(id ?? ''),\n enabled: Boolean(id),\n queryFn: async () => {\n const result = await mybQuery<{\n mybSession: (Omit<MybWorkoutSession, 'sets'> & { sets: { items: MybSessionSet[] } }) | null;\n }>(\n `query MybSession($id: ID!) {\n mybSession(id: $id) {\n ${SESSION_FIELDS}\n sets { items { ${SET_FIELDS} } }\n }\n }`,\n { id },\n apiGatewayUrl\n );\n if (!result.mybSession) return null;\n const { sets, ...session } = result.mybSession;\n return { ...session, sets: sets.items };\n },\n });\n};\n\n/**\n * The calling member's own best-ever logged set for `exerciseName` (backend:\n * `mybExerciseBestSet`, self-scoped — no memberId argument). Drives live\n * auto-PR detection in the log-set dialog (BOFF-4096): `null` means the\n * member has never logged this exercise before, so there is no record to\n * beat yet.\n */\nexport const useMybExerciseBestSet = (exerciseName: string) => {\n const { apiGatewayUrl } = useMyBodyShield();\n const trimmed = exerciseName.trim();\n return useQuery({\n queryKey: KEY.exerciseBestSet(trimmed),\n enabled: trimmed.length > 0,\n queryFn: async () => {\n const result = await mybQuery<{ mybExerciseBestSet: MybSessionSet | null }>(\n `query MybExerciseBestSet($exerciseName: String!) {\n mybExerciseBestSet(exerciseName: $exerciseName) { ${SET_FIELDS} }\n }`,\n { exerciseName: trimmed },\n apiGatewayUrl\n );\n return result.mybExerciseBestSet;\n },\n // Best-set history changes rarely mid-session; a short stale window\n // avoids refiring on every keystroke while the exercise name settles.\n staleTime: 15_000,\n });\n};\n\nexport const useMybStartSession = () => {\n const { apiGatewayUrl } = useMyBodyShield();\n const qc = useQueryClient();\n return useMutation({\n mutationFn: async (input: MybStartSessionInput) => {\n const result = await mybQuery<{ mybStartSession: MybWorkoutSession }>(\n `mutation MybStartSession($input: MybStartSessionInput!) {\n mybStartSession(input: $input) { ${SESSION_FIELDS} }\n }`,\n { input },\n apiGatewayUrl\n );\n return result.mybStartSession;\n },\n onSuccess: () => qc.invalidateQueries({ queryKey: KEY.sessions }),\n });\n};\n\nexport const useMybLogSet = () => {\n const { apiGatewayUrl } = useMyBodyShield();\n const qc = useQueryClient();\n return useMutation({\n mutationFn: async (input: MybLogSetInput) => {\n const result = await mybQuery<{ mybLogSet: MybSessionSet }>(\n `mutation MybLogSet($input: MybLogSetInput!) {\n mybLogSet(input: $input) { ${SET_FIELDS} }\n }`,\n { input },\n apiGatewayUrl\n );\n return result.mybLogSet;\n },\n onSuccess: (data) => {\n qc.invalidateQueries({ queryKey: KEY.session(data.sessionId) });\n qc.invalidateQueries({ queryKey: KEY.sessions });\n // A just-logged set may itself now be the best-ever for this exercise —\n // refresh so the next auto-PR comparison (this session or a later one)\n // sees it (BOFF-4096).\n qc.invalidateQueries({ queryKey: KEY.exerciseBestSet(data.exerciseName) });\n },\n });\n};\n\nexport const useMybCompleteSession = () => {\n const { apiGatewayUrl } = useMyBodyShield();\n const qc = useQueryClient();\n return useMutation({\n mutationFn: async (vars: { id: string; input: MybCompleteSessionInput }) => {\n const result = await mybQuery<{ mybCompleteSession: MybWorkoutSession }>(\n `mutation MybCompleteSession($id: ID!, $input: MybCompleteSessionInput!) {\n mybCompleteSession(id: $id, input: $input) { ${SESSION_FIELDS} }\n }`,\n vars,\n apiGatewayUrl\n );\n return result.mybCompleteSession;\n },\n onSuccess: (data) => {\n qc.invalidateQueries({ queryKey: KEY.session(data.id) });\n qc.invalidateQueries({ queryKey: KEY.sessions });\n },\n });\n};\n\n// ============================================================================\n// Goals (Progress)\n// ============================================================================\n\nexport interface GoalFilters {\n status?: MybGoalStatus;\n memberId?: string;\n pagination?: PaginationInput;\n}\n\nexport const useMybGoals = (filters: GoalFilters = {}) => {\n const { apiGatewayUrl } = useMyBodyShield();\n return useQuery({\n queryKey: [...KEY.goals, filters],\n queryFn: async () => {\n const result = await mybQuery<{ mybListGoals: MybConnection<MybGoal> }>(\n `query MybListGoals($status: MybGoalStatus, $memberId: ID, $pagination: PaginationInput) {\n mybListGoals(status: $status, memberId: $memberId, pagination: $pagination) {\n items { ${GOAL_FIELDS} }\n pageInfo { cursor hasMore }\n }\n }`,\n {\n status: filters.status,\n memberId: filters.memberId,\n pagination: filters.pagination ?? DEFAULT_PAGE,\n },\n apiGatewayUrl\n );\n return result.mybListGoals;\n },\n });\n};\n\nexport const useMybCreateGoal = () => {\n const { apiGatewayUrl } = useMyBodyShield();\n const qc = useQueryClient();\n return useMutation({\n mutationFn: async (input: MybCreateGoalInput) => {\n const result = await mybQuery<{ mybCreateGoal: MybGoal }>(\n `mutation MybCreateGoal($input: MybCreateGoalInput!) {\n mybCreateGoal(input: $input) { ${GOAL_FIELDS} }\n }`,\n { input },\n apiGatewayUrl\n );\n return result.mybCreateGoal;\n },\n onSuccess: () => qc.invalidateQueries({ queryKey: KEY.goals }),\n });\n};\n\nexport const useMybUpdateGoal = () => {\n const { apiGatewayUrl } = useMyBodyShield();\n const qc = useQueryClient();\n return useMutation({\n mutationFn: async (vars: { id: string; input: MybUpdateGoalInput }) => {\n const result = await mybQuery<{ mybUpdateGoal: MybGoal }>(\n `mutation MybUpdateGoal($id: ID!, $input: MybUpdateGoalInput!) {\n mybUpdateGoal(id: $id, input: $input) { ${GOAL_FIELDS} }\n }`,\n vars,\n apiGatewayUrl\n );\n return result.mybUpdateGoal;\n },\n onSuccess: () => qc.invalidateQueries({ queryKey: KEY.goals }),\n });\n};\n\nexport const useMybAchieveGoal = () => {\n const { apiGatewayUrl } = useMyBodyShield();\n const qc = useQueryClient();\n return useMutation({\n mutationFn: async (id: string) => {\n const result = await mybQuery<{ mybAchieveGoal: MybGoal }>(\n `mutation MybAchieveGoal($id: ID!) {\n mybAchieveGoal(id: $id) { ${GOAL_FIELDS} }\n }`,\n { id },\n apiGatewayUrl\n );\n return result.mybAchieveGoal;\n },\n onSuccess: () => qc.invalidateQueries({ queryKey: KEY.goals }),\n });\n};\n\n// ============================================================================\n// Nutrition (plans, meals, macros)\n// ============================================================================\n\nexport const useMybActiveNutritionPlan = (memberId?: string) => {\n const { apiGatewayUrl } = useMyBodyShield();\n return useQuery({\n queryKey: [...KEY.activePlan, memberId ?? 'me'],\n queryFn: async () => {\n const result = await mybQuery<{ mybActiveNutritionPlan: MybNutritionPlan | null }>(\n `query MybActiveNutritionPlan($memberId: ID) {\n mybActiveNutritionPlan(memberId: $memberId) { ${PLAN_FIELDS} }\n }`,\n { memberId },\n apiGatewayUrl\n );\n return result.mybActiveNutritionPlan;\n },\n });\n};\n\nexport const useMybNutritionPlan = (id: string | undefined) => {\n const { apiGatewayUrl } = useMyBodyShield();\n return useQuery({\n queryKey: KEY.nutritionPlan(id ?? ''),\n enabled: Boolean(id),\n queryFn: async () => {\n const result = await mybQuery<{ mybNutritionPlan: MybNutritionPlan | null }>(\n `query MybNutritionPlan($id: ID!) {\n mybNutritionPlan(id: $id) {\n ${PLAN_FIELDS}\n meals { ${MEAL_FIELDS} }\n }\n }`,\n { id },\n apiGatewayUrl\n );\n return result.mybNutritionPlan;\n },\n });\n};\n\nexport interface MealFilters {\n mealType?: MybMealType;\n planId?: string;\n pagination?: PaginationInput;\n}\n\nexport const useMybMeals = (filters: MealFilters = {}) => {\n const { apiGatewayUrl } = useMyBodyShield();\n return useQuery({\n queryKey: [...KEY.meals, filters],\n queryFn: async () => {\n const result = await mybQuery<{ mybListMeals: MybConnection<MybMealLog> }>(\n `query MybListMeals($mealType: MybMealType, $planId: ID, $pagination: PaginationInput) {\n mybListMeals(mealType: $mealType, planId: $planId, pagination: $pagination) {\n items { ${MEAL_FIELDS} }\n pageInfo { cursor hasMore }\n }\n }`,\n {\n mealType: filters.mealType,\n planId: filters.planId,\n pagination: filters.pagination ?? DEFAULT_PAGE,\n },\n apiGatewayUrl\n );\n return result.mybListMeals;\n },\n });\n};\n\nexport const useMybDailyMacros = (date?: string, memberId?: string) => {\n const { apiGatewayUrl } = useMyBodyShield();\n return useQuery({\n queryKey: [...KEY.dailyMacros, date ?? 'today', memberId ?? 'me'],\n queryFn: async () => {\n const result = await mybQuery<{ mybDailyMacros: MybDailyMacros }>(\n `query MybDailyMacros($date: DateTime, $memberId: ID) {\n mybDailyMacros(date: $date, memberId: $memberId) { ${MACROS_FIELDS} }\n }`,\n { date, memberId },\n apiGatewayUrl\n );\n return result.mybDailyMacros;\n },\n });\n};\n\nexport const useMybCreateNutritionPlan = () => {\n const { apiGatewayUrl } = useMyBodyShield();\n const qc = useQueryClient();\n return useMutation({\n mutationFn: async (input: MybCreateNutritionPlanInput) => {\n const result = await mybQuery<{ mybCreateNutritionPlan: MybNutritionPlan }>(\n `mutation MybCreateNutritionPlan($input: MybCreateNutritionPlanInput!) {\n mybCreateNutritionPlan(input: $input) { ${PLAN_FIELDS} }\n }`,\n { input },\n apiGatewayUrl\n );\n return result.mybCreateNutritionPlan;\n },\n onSuccess: () => {\n qc.invalidateQueries({ queryKey: KEY.activePlan });\n },\n });\n};\n\nexport const useMybUpdateNutritionPlan = () => {\n const { apiGatewayUrl } = useMyBodyShield();\n const qc = useQueryClient();\n return useMutation({\n mutationFn: async (vars: { id: string; input: MybUpdateNutritionPlanInput }) => {\n const result = await mybQuery<{ mybUpdateNutritionPlan: MybNutritionPlan }>(\n `mutation MybUpdateNutritionPlan($id: ID!, $input: MybUpdateNutritionPlanInput!) {\n mybUpdateNutritionPlan(id: $id, input: $input) { ${PLAN_FIELDS} }\n }`,\n vars,\n apiGatewayUrl\n );\n return result.mybUpdateNutritionPlan;\n },\n onSuccess: (data) => {\n qc.invalidateQueries({ queryKey: KEY.activePlan });\n qc.invalidateQueries({ queryKey: KEY.nutritionPlan(data.id) });\n },\n });\n};\n\nexport const useMybActivateNutritionPlan = () => {\n const { apiGatewayUrl } = useMyBodyShield();\n const qc = useQueryClient();\n return useMutation({\n mutationFn: async (id: string) => {\n const result = await mybQuery<{ mybActivateNutritionPlan: MybNutritionPlan }>(\n `mutation MybActivateNutritionPlan($id: ID!) {\n mybActivateNutritionPlan(id: $id) { ${PLAN_FIELDS} }\n }`,\n { id },\n apiGatewayUrl\n );\n return result.mybActivateNutritionPlan;\n },\n onSuccess: (data) => {\n qc.invalidateQueries({ queryKey: KEY.activePlan });\n qc.invalidateQueries({ queryKey: KEY.nutritionPlan(data.id) });\n },\n });\n};\n\nexport const useMybLogMeal = () => {\n const { apiGatewayUrl } = useMyBodyShield();\n const qc = useQueryClient();\n return useMutation({\n mutationFn: async (input: MybLogMealInput) => {\n const result = await mybQuery<{ mybLogMeal: MybMealLog }>(\n `mutation MybLogMeal($input: MybLogMealInput!) {\n mybLogMeal(input: $input) { ${MEAL_FIELDS} }\n }`,\n { input },\n apiGatewayUrl\n );\n return result.mybLogMeal;\n },\n onSuccess: (data) => {\n qc.invalidateQueries({ queryKey: KEY.meals });\n qc.invalidateQueries({ queryKey: KEY.dailyMacros });\n if (data.planId) qc.invalidateQueries({ queryKey: KEY.nutritionPlan(data.planId) });\n },\n });\n};\n\nexport const useMybDeleteMeal = () => {\n const { apiGatewayUrl } = useMyBodyShield();\n const qc = useQueryClient();\n return useMutation({\n mutationFn: async (id: string) => {\n const result = await mybQuery<{ mybDeleteMeal: boolean }>(\n `mutation MybDeleteMeal($id: ID!) { mybDeleteMeal(id: $id) }`,\n { id },\n apiGatewayUrl\n );\n return result.mybDeleteMeal;\n },\n onSuccess: () => {\n qc.invalidateQueries({ queryKey: KEY.meals });\n qc.invalidateQueries({ queryKey: KEY.dailyMacros });\n },\n });\n};\n\n// ============================================================================\n// Body metrics (Progress)\n// ============================================================================\n\nexport interface BodyMetricFilters {\n type?: MybBodyMetricType;\n memberId?: string;\n pagination?: PaginationInput;\n}\n\nexport const useMybBodyMetrics = (filters: BodyMetricFilters = {}) => {\n const { apiGatewayUrl } = useMyBodyShield();\n return useQuery({\n queryKey: [...KEY.bodyMetrics, filters],\n queryFn: async () => {\n const result = await mybQuery<{ mybListBodyMetrics: MybConnection<MybBodyMetric> }>(\n `query MybListBodyMetrics($type: MybBodyMetricType, $memberId: ID, $pagination: PaginationInput) {\n mybListBodyMetrics(type: $type, memberId: $memberId, pagination: $pagination) {\n items { ${METRIC_FIELDS} }\n pageInfo { cursor hasMore }\n }\n }`,\n {\n type: filters.type,\n memberId: filters.memberId,\n pagination: filters.pagination ?? DEFAULT_PAGE,\n },\n apiGatewayUrl\n );\n return result.mybListBodyMetrics;\n },\n });\n};\n\nexport const useMybRecordBodyMetric = () => {\n const { apiGatewayUrl } = useMyBodyShield();\n const qc = useQueryClient();\n return useMutation({\n mutationFn: async (input: MybRecordMetricInput) => {\n const result = await mybQuery<{ mybRecordBodyMetric: MybBodyMetric }>(\n `mutation MybRecordBodyMetric($input: MybRecordMetricInput!) {\n mybRecordBodyMetric(input: $input) { ${METRIC_FIELDS} }\n }`,\n { input },\n apiGatewayUrl\n );\n return result.mybRecordBodyMetric;\n },\n onSuccess: () => qc.invalidateQueries({ queryKey: KEY.bodyMetrics }),\n });\n};\n\n// ============================================================================\n// Recovery (Wellbeing)\n// ============================================================================\n\nexport const useMybRecoveryLogs = (metric?: MybRecoveryMetric, memberId?: string) => {\n const { apiGatewayUrl } = useMyBodyShield();\n return useQuery({\n queryKey: [...KEY.recovery, metric ?? 'all', memberId ?? 'me'],\n queryFn: async () => {\n const result = await mybQuery<{ mybListRecoveryLogs: MybConnection<MybRecoveryLog> }>(\n `query MybListRecoveryLogs($metric: MybRecoveryMetric, $memberId: ID) {\n mybListRecoveryLogs(metric: $metric, memberId: $memberId) {\n items { ${RECOVERY_FIELDS} }\n pageInfo { cursor hasMore }\n }\n }`,\n { metric, memberId },\n apiGatewayUrl\n );\n return result.mybListRecoveryLogs;\n },\n });\n};\n\nexport const useMybLogRecovery = () => {\n const { apiGatewayUrl } = useMyBodyShield();\n const qc = useQueryClient();\n return useMutation({\n mutationFn: async (input: MybLogRecoveryInput) => {\n const result = await mybQuery<{ mybLogRecovery: MybRecoveryLog }>(\n `mutation MybLogRecovery($input: MybLogRecoveryInput!) {\n mybLogRecovery(input: $input) { ${RECOVERY_FIELDS} }\n }`,\n { input },\n apiGatewayUrl\n );\n return result.mybLogRecovery;\n },\n onSuccess: () => qc.invalidateQueries({ queryKey: KEY.recovery }),\n });\n};\n\n// ============================================================================\n// Coaching (Wellbeing)\n// ============================================================================\n\nexport const useMybCoaching = (status?: MybCoachingStatus, memberId?: string) => {\n const { apiGatewayUrl } = useMyBodyShield();\n return useQuery({\n queryKey: [...KEY.coaching, status ?? 'all', memberId ?? 'me'],\n queryFn: async () => {\n const result = await mybQuery<{ mybListCoaching: MybConnection<MybCoachingRelationship> }>(\n `query MybListCoaching($status: MybCoachingStatus, $memberId: ID) {\n mybListCoaching(status: $status, memberId: $memberId) {\n items { ${COACHING_FIELDS} }\n pageInfo { cursor hasMore }\n }\n }`,\n { status, memberId },\n apiGatewayUrl\n );\n return result.mybListCoaching;\n },\n });\n};\n\nexport const useMybCreateCoaching = () => {\n const { apiGatewayUrl } = useMyBodyShield();\n const qc = useQueryClient();\n return useMutation({\n mutationFn: async (input: MybCreateCoachingInput) => {\n const result = await mybQuery<{ mybCreateCoaching: MybCoachingRelationship }>(\n `mutation MybCreateCoaching($input: MybCreateCoachingInput!) {\n mybCreateCoaching(input: $input) { ${COACHING_FIELDS} }\n }`,\n { input },\n apiGatewayUrl\n );\n return result.mybCreateCoaching;\n },\n onSuccess: () => qc.invalidateQueries({ queryKey: KEY.coaching }),\n });\n};\n\nexport const useMybTransitionCoaching = () => {\n const { apiGatewayUrl } = useMyBodyShield();\n const qc = useQueryClient();\n return useMutation({\n mutationFn: async (vars: { id: string; status: MybCoachingStatus }) => {\n const result = await mybQuery<{ mybTransitionCoaching: MybCoachingRelationship }>(\n `mutation MybTransitionCoaching($id: ID!, $status: MybCoachingStatus!) {\n mybTransitionCoaching(id: $id, status: $status) { ${COACHING_FIELDS} }\n }`,\n vars,\n apiGatewayUrl\n );\n return result.mybTransitionCoaching;\n },\n onSuccess: () => qc.invalidateQueries({ queryKey: KEY.coaching }),\n });\n};\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SessionsPage.d.ts","sourceRoot":"","sources":["../../../../src/mybodyshield/pages/train/SessionsPage.tsx"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;
|
|
1
|
+
{"version":3,"file":"SessionsPage.d.ts","sourceRoot":"","sources":["../../../../src/mybodyshield/pages/train/SessionsPage.tsx"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AA0IH,wBAAgB,YAAY,gCA0S3B"}
|
|
@@ -7,20 +7,20 @@ import { ErrorView as i, LoadingView as a } from "../../components/StateViews.js
|
|
|
7
7
|
import { FilterTabs as o } from "../../components/FilterTabs.js";
|
|
8
8
|
import { Pagination as s } from "../../components/Pagination.js";
|
|
9
9
|
import "../../context/index.js";
|
|
10
|
-
import {
|
|
11
|
-
import { useOffsetPager as
|
|
12
|
-
import { useMyBodyShieldAnalytics as
|
|
10
|
+
import { useMybProgramWorkouts as c, useMybPrograms as l, useMybSessions as u, useMybStartSession as re } from "../../hooks/useMyBodyShieldApi.js";
|
|
11
|
+
import { useOffsetPager as d } from "../../hooks/usePager.js";
|
|
12
|
+
import { useMyBodyShieldAnalytics as f } from "../../hooks/useMyBodyShieldAnalytics.js";
|
|
13
13
|
import "../../hooks/index.js";
|
|
14
14
|
import "../../components/index.js";
|
|
15
|
-
import { motion as
|
|
16
|
-
import { useReducedMotion as
|
|
17
|
-
import { IconChip as
|
|
18
|
-
import { useState as
|
|
19
|
-
import { CalendarClock as
|
|
20
|
-
import { Fragment as
|
|
21
|
-
import { Button as
|
|
15
|
+
import { motion as p } from "../../../node_modules/framer-motion/dist/es/render/components/motion/proxy.js";
|
|
16
|
+
import { useReducedMotion as m } from "../../../node_modules/framer-motion/dist/es/utils/reduced-motion/use-reduced-motion.js";
|
|
17
|
+
import { IconChip as h, Meter as ie } from "../_shared/visuals.js";
|
|
18
|
+
import { useState as g } from "react";
|
|
19
|
+
import { CalendarClock as _, CheckCircle2 as v, Flame as y, Gauge as ae, ListChecks as oe, PlayCircle as b, Plus as x, SkipForward as S } from "lucide-react";
|
|
20
|
+
import { Fragment as C, jsx as w, jsxs as T } from "react/jsx-runtime";
|
|
21
|
+
import { Button as E, GlassCard as D, Input as se, Label as O, ResponsiveDialog as ce, ResponsiveDialogContent as le, ResponsiveDialogDescription as ue, ResponsiveDialogFooter as de, ResponsiveDialogHeader as fe, ResponsiveDialogTitle as pe, Select as k, SelectContent as A, SelectItem as j, SelectTrigger as M, SelectValue as N, Textarea as P } from "@burdenoff/fe-libs/ui";
|
|
22
22
|
//#region src/mybodyshield/pages/train/SessionsPage.tsx
|
|
23
|
-
var
|
|
23
|
+
var F = [
|
|
24
24
|
{
|
|
25
25
|
value: "ALL",
|
|
26
26
|
label: "All"
|
|
@@ -37,41 +37,42 @@ var L = [
|
|
|
37
37
|
value: "COMPLETED",
|
|
38
38
|
label: "Completed"
|
|
39
39
|
}
|
|
40
|
-
],
|
|
40
|
+
], I = 24, L = [
|
|
41
41
|
.16,
|
|
42
42
|
1,
|
|
43
43
|
.3,
|
|
44
44
|
1
|
|
45
|
-
],
|
|
46
|
-
programId:
|
|
45
|
+
], R = "__none__", z = "__none__", B = {
|
|
46
|
+
programId: R,
|
|
47
|
+
workoutId: z,
|
|
47
48
|
scheduledFor: "",
|
|
48
49
|
notes: ""
|
|
49
50
|
};
|
|
50
|
-
function
|
|
51
|
+
function me(e) {
|
|
51
52
|
switch (e.status) {
|
|
52
53
|
case "IN_PROGRESS": return {
|
|
53
|
-
icon:
|
|
54
|
+
icon: b,
|
|
54
55
|
tint: "var(--color-accent-teal)",
|
|
55
56
|
title: "Live workout in progress",
|
|
56
57
|
stampLabel: "Started",
|
|
57
58
|
stamp: e.startedAt ?? e.createdAt
|
|
58
59
|
};
|
|
59
60
|
case "COMPLETED": return {
|
|
60
|
-
icon:
|
|
61
|
+
icon: v,
|
|
61
62
|
tint: "var(--color-status-success-text)",
|
|
62
63
|
title: e.caloriesBurned > 0 ? `${n(e.caloriesBurned)} burned` : "Workout completed",
|
|
63
64
|
stampLabel: "Completed",
|
|
64
65
|
stamp: e.completedAt ?? e.startedAt ?? e.createdAt
|
|
65
66
|
};
|
|
66
67
|
case "SKIPPED": return {
|
|
67
|
-
icon:
|
|
68
|
+
icon: S,
|
|
68
69
|
tint: "var(--color-text-secondary)",
|
|
69
70
|
title: "Skipped session",
|
|
70
71
|
stampLabel: "Scheduled",
|
|
71
72
|
stamp: e.scheduledFor ?? e.createdAt
|
|
72
73
|
};
|
|
73
74
|
default: return {
|
|
74
|
-
icon:
|
|
75
|
+
icon: _,
|
|
75
76
|
tint: "var(--color-accent-primary)",
|
|
76
77
|
title: "Scheduled session",
|
|
77
78
|
stampLabel: "Scheduled",
|
|
@@ -79,58 +80,58 @@ function H(e) {
|
|
|
79
80
|
};
|
|
80
81
|
}
|
|
81
82
|
}
|
|
82
|
-
function
|
|
83
|
-
let { navigateTo:
|
|
84
|
-
status:
|
|
83
|
+
function V() {
|
|
84
|
+
let { navigateTo: _, workspaceId: v } = e(), b = m(), S = f(), [V, he] = g("ALL"), H = d(I), U = u({
|
|
85
|
+
status: V === "ALL" ? void 0 : V,
|
|
85
86
|
pagination: {
|
|
86
|
-
limit:
|
|
87
|
-
offset:
|
|
87
|
+
limit: I,
|
|
88
|
+
offset: H.offset
|
|
88
89
|
}
|
|
89
|
-
}),
|
|
90
|
+
}), ge = l({
|
|
90
91
|
status: "ACTIVE",
|
|
91
92
|
pagination: { limit: 50 }
|
|
92
|
-
}).data?.items ?? [],
|
|
93
|
-
|
|
93
|
+
}).data?.items ?? [], W = re(), [_e, G] = g(!1), [K, q] = g(B), [J, Y] = g(null), X = K.programId === R ? void 0 : K.programId, Z = c(X).data ?? [], Q = U.data?.items ?? [], ve = U.data?.pageInfo?.hasMore ?? !1, ye = (e) => {
|
|
94
|
+
he(e), H.reset();
|
|
94
95
|
}, $ = () => {
|
|
95
|
-
|
|
96
|
+
q(B), Y(null), G(!0), S.emitIntent("session", "create", { workspaceId: v });
|
|
96
97
|
};
|
|
97
|
-
return /* @__PURE__ */
|
|
98
|
+
return /* @__PURE__ */ T(t, {
|
|
98
99
|
title: "Train",
|
|
99
100
|
description: "Live workout sessions — start, log sets, and complete.",
|
|
100
|
-
actions: /* @__PURE__ */
|
|
101
|
+
actions: /* @__PURE__ */ T(E, {
|
|
101
102
|
onClick: $,
|
|
102
|
-
children: [/* @__PURE__ */ x
|
|
103
|
+
children: [/* @__PURE__ */ w(x, { className: "h-4 w-4" }), " Start session"]
|
|
103
104
|
}),
|
|
104
105
|
children: [
|
|
105
|
-
/* @__PURE__ */
|
|
106
|
+
/* @__PURE__ */ w("div", {
|
|
106
107
|
className: "mb-6",
|
|
107
|
-
children: /* @__PURE__ */
|
|
108
|
-
options:
|
|
109
|
-
value:
|
|
110
|
-
onChange:
|
|
108
|
+
children: /* @__PURE__ */ w(o, {
|
|
109
|
+
options: F,
|
|
110
|
+
value: V,
|
|
111
|
+
onChange: ye
|
|
111
112
|
})
|
|
112
113
|
}),
|
|
113
|
-
|
|
114
|
+
U.isLoading ? /* @__PURE__ */ w(a, { label: "Loading your sessions…" }) : U.isError ? /* @__PURE__ */ w(i, {
|
|
114
115
|
message: "We couldn't load your workout sessions.",
|
|
115
|
-
onRetry: () =>
|
|
116
|
-
}) : Q.length === 0 ? /* @__PURE__ */
|
|
116
|
+
onRetry: () => U.refetch()
|
|
117
|
+
}) : Q.length === 0 ? /* @__PURE__ */ w(r, {
|
|
117
118
|
illustration: "empty-data",
|
|
118
|
-
icon: /* @__PURE__ */
|
|
119
|
-
title:
|
|
120
|
-
description:
|
|
121
|
-
action: /* @__PURE__ */
|
|
119
|
+
icon: /* @__PURE__ */ w(y, { className: "h-10 w-10" }),
|
|
120
|
+
title: V === "ALL" ? "No sessions yet" : "No sessions in this state",
|
|
121
|
+
description: V === "ALL" ? "Start a workout session to begin logging sets and tracking your training." : "Try a different filter, or start a new workout session.",
|
|
122
|
+
action: /* @__PURE__ */ T(E, {
|
|
122
123
|
onClick: $,
|
|
123
|
-
children: [/* @__PURE__ */ x
|
|
124
|
+
children: [/* @__PURE__ */ w(x, { className: "h-4 w-4" }), " Start session"]
|
|
124
125
|
})
|
|
125
|
-
}) : /* @__PURE__ */
|
|
126
|
+
}) : /* @__PURE__ */ T(C, { children: [/* @__PURE__ */ w("div", {
|
|
126
127
|
className: "grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3",
|
|
127
128
|
children: Q.map((e, t) => {
|
|
128
|
-
let r =
|
|
129
|
-
return /* @__PURE__ */
|
|
129
|
+
let r = me(e), i = r.icon, a = e.status === "IN_PROGRESS", o = e.sets?.length, s = e.perceivedExertion;
|
|
130
|
+
return /* @__PURE__ */ w(p.button, {
|
|
130
131
|
type: "button",
|
|
131
|
-
onClick: () =>
|
|
132
|
+
onClick: () => _(`train/${e.id}`),
|
|
132
133
|
className: "block w-full text-left",
|
|
133
|
-
initial:
|
|
134
|
+
initial: b ? !1 : {
|
|
134
135
|
opacity: 0,
|
|
135
136
|
y: 12
|
|
136
137
|
},
|
|
@@ -138,30 +139,30 @@ function U() {
|
|
|
138
139
|
opacity: 1,
|
|
139
140
|
y: 0
|
|
140
141
|
},
|
|
141
|
-
transition:
|
|
142
|
+
transition: b ? { duration: 0 } : {
|
|
142
143
|
duration: .35,
|
|
143
144
|
delay: Math.min(t, 8) * .05,
|
|
144
|
-
ease:
|
|
145
|
+
ease: L
|
|
145
146
|
},
|
|
146
|
-
whileHover:
|
|
147
|
-
whileTap:
|
|
148
|
-
children: /* @__PURE__ */
|
|
147
|
+
whileHover: b ? void 0 : { y: -3 },
|
|
148
|
+
whileTap: b ? void 0 : { scale: .995 },
|
|
149
|
+
children: /* @__PURE__ */ T(D, {
|
|
149
150
|
treatment: "plain",
|
|
150
151
|
glow: a,
|
|
151
152
|
className: "h-full p-4 transition-shadow hover:shadow-float" + (a ? " ring-1 ring-inset ring-[color-mix(in_oklab,var(--color-action-primary-bg)_45%,transparent)]" : ""),
|
|
152
153
|
children: [
|
|
153
|
-
/* @__PURE__ */
|
|
154
|
+
/* @__PURE__ */ T("div", {
|
|
154
155
|
className: "flex items-center justify-between gap-2",
|
|
155
|
-
children: [/* @__PURE__ */
|
|
156
|
-
icon: /* @__PURE__ */
|
|
156
|
+
children: [/* @__PURE__ */ w(h, {
|
|
157
|
+
icon: /* @__PURE__ */ w(i, { className: "h-4 w-4" }),
|
|
157
158
|
tint: r.tint
|
|
158
|
-
}), /* @__PURE__ */
|
|
159
|
+
}), /* @__PURE__ */ w(ne, { status: e.status })]
|
|
159
160
|
}),
|
|
160
|
-
/* @__PURE__ */
|
|
161
|
+
/* @__PURE__ */ w("p", {
|
|
161
162
|
className: "mt-3 text-base font-semibold text-text-primary",
|
|
162
163
|
children: r.title
|
|
163
164
|
}),
|
|
164
|
-
/* @__PURE__ */
|
|
165
|
+
/* @__PURE__ */ T("p", {
|
|
165
166
|
className: "mt-0.5 text-xs text-text-tertiary",
|
|
166
167
|
children: [
|
|
167
168
|
r.stampLabel,
|
|
@@ -169,46 +170,46 @@ function U() {
|
|
|
169
170
|
te(r.stamp)
|
|
170
171
|
]
|
|
171
172
|
}),
|
|
172
|
-
/* @__PURE__ */
|
|
173
|
+
/* @__PURE__ */ T("div", {
|
|
173
174
|
className: "mt-3 flex flex-wrap items-center gap-x-4 gap-y-1 text-xs text-text-secondary",
|
|
174
175
|
children: [
|
|
175
|
-
e.caloriesBurned > 0 && /* @__PURE__ */
|
|
176
|
+
e.caloriesBurned > 0 && /* @__PURE__ */ T("span", {
|
|
176
177
|
className: "inline-flex items-center gap-1 tabular-nums",
|
|
177
|
-
children: [/* @__PURE__ */
|
|
178
|
+
children: [/* @__PURE__ */ w(y, { className: "h-3.5 w-3.5 text-status-warning-text" }), n(e.caloriesBurned)]
|
|
178
179
|
}),
|
|
179
|
-
s != null && /* @__PURE__ */
|
|
180
|
+
s != null && /* @__PURE__ */ T("span", {
|
|
180
181
|
className: "inline-flex items-center gap-1 tabular-nums",
|
|
181
182
|
children: [
|
|
182
|
-
/* @__PURE__ */
|
|
183
|
+
/* @__PURE__ */ w(ae, { className: "h-3.5 w-3.5 text-accent" }),
|
|
183
184
|
"RPE ",
|
|
184
185
|
s,
|
|
185
186
|
"/10"
|
|
186
187
|
]
|
|
187
188
|
}),
|
|
188
|
-
o != null && o > 0 && /* @__PURE__ */
|
|
189
|
+
o != null && o > 0 && /* @__PURE__ */ T("span", {
|
|
189
190
|
className: "inline-flex items-center gap-1 tabular-nums",
|
|
190
191
|
children: [
|
|
191
|
-
/* @__PURE__ */
|
|
192
|
+
/* @__PURE__ */ w(oe, { className: "h-3.5 w-3.5 text-accent" }),
|
|
192
193
|
ee(o),
|
|
193
194
|
" sets"
|
|
194
195
|
]
|
|
195
196
|
})
|
|
196
197
|
]
|
|
197
198
|
}),
|
|
198
|
-
s != null && /* @__PURE__ */
|
|
199
|
+
s != null && /* @__PURE__ */ T("div", {
|
|
199
200
|
className: "mt-3",
|
|
200
|
-
children: [/* @__PURE__ */
|
|
201
|
+
children: [/* @__PURE__ */ T("div", {
|
|
201
202
|
className: "mb-1 flex items-center justify-between text-[11px] text-text-tertiary",
|
|
202
|
-
children: [/* @__PURE__ */
|
|
203
|
+
children: [/* @__PURE__ */ w("span", { children: "RPE intensity" }), /* @__PURE__ */ T("span", {
|
|
203
204
|
className: "tabular-nums",
|
|
204
205
|
children: [s, "/10"]
|
|
205
206
|
})]
|
|
206
|
-
}), /* @__PURE__ */
|
|
207
|
+
}), /* @__PURE__ */ w(ie, {
|
|
207
208
|
value: s * 10,
|
|
208
209
|
height: 6
|
|
209
210
|
})]
|
|
210
211
|
}),
|
|
211
|
-
a && /* @__PURE__ */
|
|
212
|
+
a && /* @__PURE__ */ w("p", {
|
|
212
213
|
className: "mt-3 inline-flex items-center gap-1 text-xs font-semibold text-action-primary-bg",
|
|
213
214
|
children: "In progress — Resume →"
|
|
214
215
|
})
|
|
@@ -216,112 +217,150 @@ function U() {
|
|
|
216
217
|
})
|
|
217
218
|
}, e.id);
|
|
218
219
|
})
|
|
219
|
-
}), /* @__PURE__ */
|
|
220
|
-
page:
|
|
221
|
-
pageSize:
|
|
220
|
+
}), /* @__PURE__ */ w(s, {
|
|
221
|
+
page: H.page,
|
|
222
|
+
pageSize: I,
|
|
222
223
|
itemCount: Q.length,
|
|
223
|
-
hasMore:
|
|
224
|
-
onPageChange: (e) =>
|
|
224
|
+
hasMore: ve,
|
|
225
|
+
onPageChange: (e) => H.onPageChange(e)
|
|
225
226
|
})] }),
|
|
226
|
-
/* @__PURE__ */
|
|
227
|
-
open:
|
|
228
|
-
onOpenChange:
|
|
229
|
-
children: /* @__PURE__ */
|
|
230
|
-
/* @__PURE__ */
|
|
231
|
-
/* @__PURE__ */
|
|
227
|
+
/* @__PURE__ */ w(ce, {
|
|
228
|
+
open: _e,
|
|
229
|
+
onOpenChange: G,
|
|
230
|
+
children: /* @__PURE__ */ T(le, { children: [
|
|
231
|
+
/* @__PURE__ */ T(fe, { children: [/* @__PURE__ */ w(pe, { children: "Start a workout session" }), /* @__PURE__ */ w(ue, { children: "Begin a live session now, or schedule one for later. Attach an active program to keep your training organised." })] }),
|
|
232
|
+
/* @__PURE__ */ T("div", {
|
|
232
233
|
className: "space-y-4 px-1",
|
|
233
234
|
children: [
|
|
234
|
-
/* @__PURE__ */
|
|
235
|
+
/* @__PURE__ */ T("div", {
|
|
235
236
|
className: "space-y-1.5",
|
|
236
|
-
children: [/* @__PURE__ */
|
|
237
|
+
children: [/* @__PURE__ */ w(O, {
|
|
237
238
|
htmlFor: "myb-start-program",
|
|
238
239
|
children: "Program (optional)"
|
|
239
|
-
}), /* @__PURE__ */
|
|
240
|
-
value:
|
|
241
|
-
onValueChange: (e) =>
|
|
240
|
+
}), /* @__PURE__ */ T(k, {
|
|
241
|
+
value: K.programId,
|
|
242
|
+
onValueChange: (e) => q((t) => ({
|
|
242
243
|
...t,
|
|
243
|
-
programId: e
|
|
244
|
+
programId: e,
|
|
245
|
+
workoutId: z
|
|
244
246
|
})),
|
|
245
|
-
children: [/* @__PURE__ */
|
|
247
|
+
children: [/* @__PURE__ */ w(M, {
|
|
246
248
|
id: "myb-start-program",
|
|
247
|
-
children: /* @__PURE__ */
|
|
248
|
-
}), /* @__PURE__ */
|
|
249
|
-
value:
|
|
249
|
+
children: /* @__PURE__ */ w(N, { placeholder: "No program" })
|
|
250
|
+
}), /* @__PURE__ */ T(A, { children: [/* @__PURE__ */ w(j, {
|
|
251
|
+
value: R,
|
|
250
252
|
children: "No program"
|
|
251
|
-
}),
|
|
253
|
+
}), ge.map((e) => /* @__PURE__ */ w(j, {
|
|
252
254
|
value: e.id,
|
|
253
255
|
children: e.name
|
|
254
256
|
}, e.id))] })]
|
|
255
257
|
})]
|
|
256
258
|
}),
|
|
257
|
-
/* @__PURE__ */
|
|
259
|
+
X && Z.length > 0 && /* @__PURE__ */ T("div", {
|
|
260
|
+
className: "space-y-1.5",
|
|
261
|
+
children: [
|
|
262
|
+
/* @__PURE__ */ w(O, {
|
|
263
|
+
htmlFor: "myb-start-workout",
|
|
264
|
+
children: "Workout (optional)"
|
|
265
|
+
}),
|
|
266
|
+
/* @__PURE__ */ T(k, {
|
|
267
|
+
value: K.workoutId,
|
|
268
|
+
onValueChange: (e) => q((t) => ({
|
|
269
|
+
...t,
|
|
270
|
+
workoutId: e
|
|
271
|
+
})),
|
|
272
|
+
children: [/* @__PURE__ */ w(M, {
|
|
273
|
+
id: "myb-start-workout",
|
|
274
|
+
children: /* @__PURE__ */ w(N, { placeholder: "No specific workout" })
|
|
275
|
+
}), /* @__PURE__ */ T(A, { children: [/* @__PURE__ */ w(j, {
|
|
276
|
+
value: z,
|
|
277
|
+
children: "No specific workout"
|
|
278
|
+
}), Z.map((e) => /* @__PURE__ */ T(j, {
|
|
279
|
+
value: e.id,
|
|
280
|
+
children: [
|
|
281
|
+
e.name,
|
|
282
|
+
" — Week ",
|
|
283
|
+
e.weekNumber,
|
|
284
|
+
", Day ",
|
|
285
|
+
e.dayOfWeek
|
|
286
|
+
]
|
|
287
|
+
}, e.id))] })]
|
|
288
|
+
}),
|
|
289
|
+
/* @__PURE__ */ w("p", {
|
|
290
|
+
className: "text-xs text-text-tertiary",
|
|
291
|
+
children: "Attach a planned workout to guide this session exercise-by-exercise."
|
|
292
|
+
})
|
|
293
|
+
]
|
|
294
|
+
}),
|
|
295
|
+
/* @__PURE__ */ T("div", {
|
|
258
296
|
className: "space-y-1.5",
|
|
259
297
|
children: [
|
|
260
|
-
/* @__PURE__ */
|
|
298
|
+
/* @__PURE__ */ w(O, {
|
|
261
299
|
htmlFor: "myb-start-scheduled",
|
|
262
300
|
children: "Scheduled for (optional)"
|
|
263
301
|
}),
|
|
264
|
-
/* @__PURE__ */
|
|
302
|
+
/* @__PURE__ */ w(se, {
|
|
265
303
|
id: "myb-start-scheduled",
|
|
266
304
|
type: "datetime-local",
|
|
267
|
-
value:
|
|
268
|
-
onChange: (e) =>
|
|
305
|
+
value: K.scheduledFor,
|
|
306
|
+
onChange: (e) => q((t) => ({
|
|
269
307
|
...t,
|
|
270
308
|
scheduledFor: e.target.value
|
|
271
309
|
}))
|
|
272
310
|
}),
|
|
273
|
-
/* @__PURE__ */
|
|
311
|
+
/* @__PURE__ */ w("p", {
|
|
274
312
|
className: "text-xs text-text-tertiary",
|
|
275
313
|
children: "Leave empty to start a live session right now."
|
|
276
314
|
})
|
|
277
315
|
]
|
|
278
316
|
}),
|
|
279
|
-
/* @__PURE__ */
|
|
317
|
+
/* @__PURE__ */ T("div", {
|
|
280
318
|
className: "space-y-1.5",
|
|
281
|
-
children: [/* @__PURE__ */
|
|
319
|
+
children: [/* @__PURE__ */ w(O, {
|
|
282
320
|
htmlFor: "myb-start-notes",
|
|
283
321
|
children: "Notes (optional)"
|
|
284
|
-
}), /* @__PURE__ */
|
|
322
|
+
}), /* @__PURE__ */ w(P, {
|
|
285
323
|
id: "myb-start-notes",
|
|
286
324
|
rows: 3,
|
|
287
|
-
value:
|
|
288
|
-
onChange: (e) =>
|
|
325
|
+
value: K.notes,
|
|
326
|
+
onChange: (e) => q((t) => ({
|
|
289
327
|
...t,
|
|
290
328
|
notes: e.target.value
|
|
291
329
|
})),
|
|
292
330
|
placeholder: "Focus, target lifts, how you're feeling…"
|
|
293
331
|
})]
|
|
294
332
|
}),
|
|
295
|
-
|
|
333
|
+
J && /* @__PURE__ */ w("p", {
|
|
296
334
|
className: "text-sm text-status-error-text",
|
|
297
|
-
children:
|
|
335
|
+
children: J
|
|
298
336
|
})
|
|
299
337
|
]
|
|
300
338
|
}),
|
|
301
|
-
/* @__PURE__ */
|
|
339
|
+
/* @__PURE__ */ T(de, { children: [/* @__PURE__ */ w(E, {
|
|
302
340
|
variant: "outline",
|
|
303
|
-
onClick: () =>
|
|
304
|
-
disabled:
|
|
341
|
+
onClick: () => G(!1),
|
|
342
|
+
disabled: W.isPending,
|
|
305
343
|
children: "Cancel"
|
|
306
|
-
}), /* @__PURE__ */
|
|
344
|
+
}), /* @__PURE__ */ w(E, {
|
|
307
345
|
onClick: async () => {
|
|
308
|
-
|
|
346
|
+
Y(null);
|
|
309
347
|
try {
|
|
310
|
-
let e = await
|
|
311
|
-
programId:
|
|
312
|
-
|
|
313
|
-
|
|
348
|
+
let e = await W.mutateAsync({
|
|
349
|
+
programId: X,
|
|
350
|
+
workoutId: K.workoutId === z ? void 0 : K.workoutId,
|
|
351
|
+
scheduledFor: K.scheduledFor ? new Date(K.scheduledFor).toISOString() : void 0,
|
|
352
|
+
notes: K.notes.trim() ? K.notes.trim() : void 0
|
|
314
353
|
});
|
|
315
|
-
|
|
316
|
-
workspaceId:
|
|
354
|
+
S.emitFact("session", "started", {
|
|
355
|
+
workspaceId: v,
|
|
317
356
|
entityId: e.id
|
|
318
|
-
}),
|
|
357
|
+
}), G(!1), _(`train/${e.id}`);
|
|
319
358
|
} catch (e) {
|
|
320
|
-
|
|
359
|
+
Y(e instanceof Error ? e.message : "Failed to start the session.");
|
|
321
360
|
}
|
|
322
361
|
},
|
|
323
|
-
disabled:
|
|
324
|
-
children:
|
|
362
|
+
disabled: W.isPending,
|
|
363
|
+
children: W.isPending ? "Starting…" : "Start session"
|
|
325
364
|
})] })
|
|
326
365
|
] })
|
|
327
366
|
})
|
|
@@ -329,6 +368,6 @@ function U() {
|
|
|
329
368
|
});
|
|
330
369
|
}
|
|
331
370
|
//#endregion
|
|
332
|
-
export {
|
|
371
|
+
export { V as SessionsPage };
|
|
333
372
|
|
|
334
373
|
//# sourceMappingURL=SessionsPage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SessionsPage.js","names":[],"sources":["../../../../src/mybodyshield/pages/train/SessionsPage.tsx"],"sourcesContent":["/**\n * Train — live workout sessions.\n * @module mybodyshield/pages/train\n *\n * Lists the member's workout sessions (real `mybListSessions`) filtered by\n * status, and lets you start a new live session (optionally attached to an\n * active program). In-progress sessions are highlighted with an accent ring and\n * a \"Resume\" affordance. No mock data — everything comes from the myb* hooks.\n */\n\nimport { useState } from 'react';\nimport { motion, useReducedMotion } from 'framer-motion';\nimport {\n Plus,\n Flame,\n CheckCircle2,\n CalendarClock,\n PlayCircle,\n SkipForward,\n Gauge,\n ListChecks,\n} from 'lucide-react';\nimport {\n Button,\n GlassCard,\n Label,\n Textarea,\n Input,\n Select,\n SelectTrigger,\n SelectContent,\n SelectItem,\n SelectValue,\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n ResponsiveDialogDescription,\n ResponsiveDialogFooter,\n} from '@burdenoff/fe-libs/ui';\nimport {\n PageLayout,\n StatusBadge,\n FilterTabs,\n Pagination,\n LoadingView,\n ErrorView,\n EmptyState,\n type FilterOption,\n} from '../../components';\nimport { IconChip, Meter } from '../_shared/visuals';\nimport { useMyBodyShield } from '../../context';\nimport {\n useMybSessions,\n useMybStartSession,\n useMybPrograms,\n useOffsetPager,\n useMyBodyShieldAnalytics,\n} from '../../hooks';\nimport { formatRelativeTime, formatKcal, formatNumber } from '../../utils/formatters';\nimport type { MybSessionStatus, MybWorkoutSession } from '../../types';\n\ntype SessionFilterValue = MybSessionStatus | 'ALL';\n\n// Only statuses a control can actually reach are offered as filters: start →\n// IN_PROGRESS / SCHEDULED, complete → COMPLETED. Nothing moves a session to\n// SKIPPED, so that tab would always be empty. sessionMeta still handles the\n// SKIPPED case for display safety.\nconst STATUS_TABS: FilterOption<SessionFilterValue>[] = [\n { value: 'ALL', label: 'All' },\n { value: 'SCHEDULED', label: 'Scheduled' },\n { value: 'IN_PROGRESS', label: 'Live' },\n { value: 'COMPLETED', label: 'Completed' },\n];\n\nconst PAGE_SIZE = 24;\n\n/** Shared entrance easing — a soft, decelerating cubic-bezier. */\nconst MOTION_EASE: [number, number, number, number] = [0.16, 1, 0.3, 1];\n\n/** Sentinel for the optional program picker (Radix Select forbids an empty value). */\nconst NO_PROGRAM = '__none__';\n\ninterface StartForm {\n programId: string;\n scheduledFor: string;\n notes: string;\n}\n\nconst EMPTY_START_FORM: StartForm = { programId: NO_PROGRAM, scheduledFor: '', notes: '' };\n\ninterface SessionMeta {\n icon: typeof Flame;\n tint: string;\n title: string;\n stampLabel: string;\n stamp?: string | null;\n}\n\nfunction sessionMeta(s: MybWorkoutSession): SessionMeta {\n switch (s.status) {\n case 'IN_PROGRESS':\n return {\n icon: PlayCircle,\n tint: 'var(--color-accent-teal)',\n title: 'Live workout in progress',\n stampLabel: 'Started',\n stamp: s.startedAt ?? s.createdAt,\n };\n case 'COMPLETED':\n return {\n icon: CheckCircle2,\n tint: 'var(--color-status-success-text)',\n title:\n s.caloriesBurned > 0 ? `${formatKcal(s.caloriesBurned)} burned` : 'Workout completed',\n stampLabel: 'Completed',\n stamp: s.completedAt ?? s.startedAt ?? s.createdAt,\n };\n case 'SKIPPED':\n return {\n icon: SkipForward,\n tint: 'var(--color-text-secondary)',\n title: 'Skipped session',\n stampLabel: 'Scheduled',\n stamp: s.scheduledFor ?? s.createdAt,\n };\n case 'SCHEDULED':\n default:\n return {\n icon: CalendarClock,\n tint: 'var(--color-accent-primary)',\n title: 'Scheduled session',\n stampLabel: 'Scheduled',\n stamp: s.scheduledFor ?? s.createdAt,\n };\n }\n}\n\nexport function SessionsPage() {\n const { navigateTo, workspaceId } = useMyBodyShield();\n const reduce = useReducedMotion();\n const analytics = useMyBodyShieldAnalytics();\n\n const [filter, setFilter] = useState<SessionFilterValue>('ALL');\n const pager = useOffsetPager(PAGE_SIZE);\n\n const sessions = useMybSessions({\n status: filter === 'ALL' ? undefined : filter,\n pagination: { limit: PAGE_SIZE, offset: pager.offset },\n });\n\n const programs = useMybPrograms({ status: 'ACTIVE', pagination: { limit: 50 } });\n const activePrograms = programs.data?.items ?? [];\n\n const startSession = useMybStartSession();\n\n const [startOpen, setStartOpen] = useState(false);\n const [form, setForm] = useState<StartForm>(EMPTY_START_FORM);\n const [formError, setFormError] = useState<string | null>(null);\n\n const rows = sessions.data?.items ?? [];\n const hasMore = sessions.data?.pageInfo?.hasMore ?? false;\n\n const onFilterChange = (value: SessionFilterValue) => {\n setFilter(value);\n pager.reset();\n };\n\n const openStart = () => {\n setForm(EMPTY_START_FORM);\n setFormError(null);\n setStartOpen(true);\n analytics.emitIntent('session', 'create', { workspaceId });\n };\n\n const submitStart = async () => {\n setFormError(null);\n try {\n const created = await startSession.mutateAsync({\n programId: form.programId === NO_PROGRAM ? undefined : form.programId,\n scheduledFor: form.scheduledFor ? new Date(form.scheduledFor).toISOString() : undefined,\n notes: form.notes.trim() ? form.notes.trim() : undefined,\n });\n analytics.emitFact('session', 'started', { workspaceId, entityId: created.id });\n setStartOpen(false);\n navigateTo(`train/${created.id}`);\n } catch (e) {\n setFormError(e instanceof Error ? e.message : 'Failed to start the session.');\n }\n };\n\n return (\n <PageLayout\n title=\"Train\"\n description=\"Live workout sessions — start, log sets, and complete.\"\n actions={\n <Button onClick={openStart}>\n <Plus className=\"h-4 w-4\" /> Start session\n </Button>\n }\n >\n <div className=\"mb-6\">\n <FilterTabs options={STATUS_TABS} value={filter} onChange={onFilterChange} />\n </div>\n\n {sessions.isLoading ? (\n <LoadingView label=\"Loading your sessions…\" />\n ) : sessions.isError ? (\n <ErrorView\n message=\"We couldn't load your workout sessions.\"\n onRetry={() => sessions.refetch()}\n />\n ) : rows.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n icon={<Flame className=\"h-10 w-10\" />}\n title={filter === 'ALL' ? 'No sessions yet' : 'No sessions in this state'}\n description={\n filter === 'ALL'\n ? 'Start a workout session to begin logging sets and tracking your training.'\n : 'Try a different filter, or start a new workout session.'\n }\n action={\n <Button onClick={openStart}>\n <Plus className=\"h-4 w-4\" /> Start session\n </Button>\n }\n />\n ) : (\n <>\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n {rows.map((s, i) => {\n const meta = sessionMeta(s);\n const Icon = meta.icon;\n const isLive = s.status === 'IN_PROGRESS';\n const setCount = s.sets?.length;\n const rpe = s.perceivedExertion;\n return (\n <motion.button\n key={s.id}\n type=\"button\"\n onClick={() => navigateTo(`train/${s.id}`)}\n className=\"block w-full text-left\"\n initial={reduce ? false : { opacity: 0, y: 12 }}\n animate={{ opacity: 1, y: 0 }}\n transition={\n reduce\n ? { duration: 0 }\n : { duration: 0.35, delay: Math.min(i, 8) * 0.05, ease: MOTION_EASE }\n }\n whileHover={reduce ? undefined : { y: -3 }}\n whileTap={reduce ? undefined : { scale: 0.995 }}\n >\n <GlassCard\n treatment=\"plain\"\n glow={isLive}\n className={\n 'h-full p-4 transition-shadow hover:shadow-float' +\n (isLive\n ? ' ring-1 ring-inset ring-[color-mix(in_oklab,var(--color-action-primary-bg)_45%,transparent)]'\n : '')\n }\n >\n <div className=\"flex items-center justify-between gap-2\">\n <IconChip icon={<Icon className=\"h-4 w-4\" />} tint={meta.tint} />\n <StatusBadge status={s.status} />\n </div>\n <p className=\"mt-3 text-base font-semibold text-text-primary\">{meta.title}</p>\n <p className=\"mt-0.5 text-xs text-text-tertiary\">\n {meta.stampLabel} {formatRelativeTime(meta.stamp)}\n </p>\n\n <div className=\"mt-3 flex flex-wrap items-center gap-x-4 gap-y-1 text-xs text-text-secondary\">\n {s.caloriesBurned > 0 && (\n <span className=\"inline-flex items-center gap-1 tabular-nums\">\n <Flame className=\"h-3.5 w-3.5 text-status-warning-text\" />\n {formatKcal(s.caloriesBurned)}\n </span>\n )}\n {rpe != null && (\n <span className=\"inline-flex items-center gap-1 tabular-nums\">\n <Gauge className=\"h-3.5 w-3.5 text-accent\" />\n RPE {rpe}/10\n </span>\n )}\n {setCount != null && setCount > 0 && (\n <span className=\"inline-flex items-center gap-1 tabular-nums\">\n <ListChecks className=\"h-3.5 w-3.5 text-accent\" />\n {formatNumber(setCount)} sets\n </span>\n )}\n </div>\n\n {rpe != null && (\n <div className=\"mt-3\">\n <div className=\"mb-1 flex items-center justify-between text-[11px] text-text-tertiary\">\n <span>RPE intensity</span>\n <span className=\"tabular-nums\">{rpe}/10</span>\n </div>\n <Meter value={rpe * 10} height={6} />\n </div>\n )}\n\n {isLive && (\n <p className=\"mt-3 inline-flex items-center gap-1 text-xs font-semibold text-action-primary-bg\">\n In progress — Resume →\n </p>\n )}\n </GlassCard>\n </motion.button>\n );\n })}\n </div>\n\n <Pagination\n page={pager.page}\n pageSize={PAGE_SIZE}\n itemCount={rows.length}\n hasMore={hasMore}\n onPageChange={(t) => pager.onPageChange(t)}\n />\n </>\n )}\n\n {/* Start session dialog */}\n <ResponsiveDialog open={startOpen} onOpenChange={setStartOpen}>\n <ResponsiveDialogContent>\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>Start a workout session</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n Begin a live session now, or schedule one for later. Attach an active program to keep\n your training organised.\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n\n <div className=\"space-y-4 px-1\">\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-start-program\">Program (optional)</Label>\n <Select\n value={form.programId}\n onValueChange={(v) => setForm((f) => ({ ...f, programId: v }))}\n >\n <SelectTrigger id=\"myb-start-program\">\n <SelectValue placeholder=\"No program\" />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value={NO_PROGRAM}>No program</SelectItem>\n {activePrograms.map((p) => (\n <SelectItem key={p.id} value={p.id}>\n {p.name}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-start-scheduled\">Scheduled for (optional)</Label>\n <Input\n id=\"myb-start-scheduled\"\n type=\"datetime-local\"\n value={form.scheduledFor}\n onChange={(e) => setForm((f) => ({ ...f, scheduledFor: e.target.value }))}\n />\n <p className=\"text-xs text-text-tertiary\">\n Leave empty to start a live session right now.\n </p>\n </div>\n\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-start-notes\">Notes (optional)</Label>\n <Textarea\n id=\"myb-start-notes\"\n rows={3}\n value={form.notes}\n onChange={(e) => setForm((f) => ({ ...f, notes: e.target.value }))}\n placeholder=\"Focus, target lifts, how you're feeling…\"\n />\n </div>\n\n {formError && <p className=\"text-sm text-status-error-text\">{formError}</p>}\n </div>\n\n <ResponsiveDialogFooter>\n <Button\n variant=\"outline\"\n onClick={() => setStartOpen(false)}\n disabled={startSession.isPending}\n >\n Cancel\n </Button>\n <Button onClick={submitStart} disabled={startSession.isPending}>\n {startSession.isPending ? 'Starting…' : 'Start session'}\n </Button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"SessionsPage.js","names":[],"sources":["../../../../src/mybodyshield/pages/train/SessionsPage.tsx"],"sourcesContent":["/**\n * Train — live workout sessions.\n * @module mybodyshield/pages/train\n *\n * Lists the member's workout sessions (real `mybListSessions`) filtered by\n * status, and lets you start a new live session (optionally attached to an\n * active program). In-progress sessions are highlighted with an accent ring and\n * a \"Resume\" affordance. No mock data — everything comes from the myb* hooks.\n */\n\nimport { useState } from 'react';\nimport { motion, useReducedMotion } from 'framer-motion';\nimport {\n Plus,\n Flame,\n CheckCircle2,\n CalendarClock,\n PlayCircle,\n SkipForward,\n Gauge,\n ListChecks,\n} from 'lucide-react';\nimport {\n Button,\n GlassCard,\n Label,\n Textarea,\n Input,\n Select,\n SelectTrigger,\n SelectContent,\n SelectItem,\n SelectValue,\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n ResponsiveDialogDescription,\n ResponsiveDialogFooter,\n} from '@burdenoff/fe-libs/ui';\nimport {\n PageLayout,\n StatusBadge,\n FilterTabs,\n Pagination,\n LoadingView,\n ErrorView,\n EmptyState,\n type FilterOption,\n} from '../../components';\nimport { IconChip, Meter } from '../_shared/visuals';\nimport { useMyBodyShield } from '../../context';\nimport {\n useMybSessions,\n useMybStartSession,\n useMybPrograms,\n useMybProgramWorkouts,\n useOffsetPager,\n useMyBodyShieldAnalytics,\n} from '../../hooks';\nimport { formatRelativeTime, formatKcal, formatNumber } from '../../utils/formatters';\nimport type { MybSessionStatus, MybWorkoutSession } from '../../types';\n\ntype SessionFilterValue = MybSessionStatus | 'ALL';\n\n// Only statuses a control can actually reach are offered as filters: start →\n// IN_PROGRESS / SCHEDULED, complete → COMPLETED. Nothing moves a session to\n// SKIPPED, so that tab would always be empty. sessionMeta still handles the\n// SKIPPED case for display safety.\nconst STATUS_TABS: FilterOption<SessionFilterValue>[] = [\n { value: 'ALL', label: 'All' },\n { value: 'SCHEDULED', label: 'Scheduled' },\n { value: 'IN_PROGRESS', label: 'Live' },\n { value: 'COMPLETED', label: 'Completed' },\n];\n\nconst PAGE_SIZE = 24;\n\n/** Shared entrance easing — a soft, decelerating cubic-bezier. */\nconst MOTION_EASE: [number, number, number, number] = [0.16, 1, 0.3, 1];\n\n/** Sentinel for the optional program/workout pickers (Radix Select forbids an empty value). */\nconst NO_PROGRAM = '__none__';\nconst NO_WORKOUT = '__none__';\n\ninterface StartForm {\n programId: string;\n workoutId: string;\n scheduledFor: string;\n notes: string;\n}\n\nconst EMPTY_START_FORM: StartForm = {\n programId: NO_PROGRAM,\n workoutId: NO_WORKOUT,\n scheduledFor: '',\n notes: '',\n};\n\ninterface SessionMeta {\n icon: typeof Flame;\n tint: string;\n title: string;\n stampLabel: string;\n stamp?: string | null;\n}\n\nfunction sessionMeta(s: MybWorkoutSession): SessionMeta {\n switch (s.status) {\n case 'IN_PROGRESS':\n return {\n icon: PlayCircle,\n tint: 'var(--color-accent-teal)',\n title: 'Live workout in progress',\n stampLabel: 'Started',\n stamp: s.startedAt ?? s.createdAt,\n };\n case 'COMPLETED':\n return {\n icon: CheckCircle2,\n tint: 'var(--color-status-success-text)',\n title:\n s.caloriesBurned > 0 ? `${formatKcal(s.caloriesBurned)} burned` : 'Workout completed',\n stampLabel: 'Completed',\n stamp: s.completedAt ?? s.startedAt ?? s.createdAt,\n };\n case 'SKIPPED':\n return {\n icon: SkipForward,\n tint: 'var(--color-text-secondary)',\n title: 'Skipped session',\n stampLabel: 'Scheduled',\n stamp: s.scheduledFor ?? s.createdAt,\n };\n case 'SCHEDULED':\n default:\n return {\n icon: CalendarClock,\n tint: 'var(--color-accent-primary)',\n title: 'Scheduled session',\n stampLabel: 'Scheduled',\n stamp: s.scheduledFor ?? s.createdAt,\n };\n }\n}\n\nexport function SessionsPage() {\n const { navigateTo, workspaceId } = useMyBodyShield();\n const reduce = useReducedMotion();\n const analytics = useMyBodyShieldAnalytics();\n\n const [filter, setFilter] = useState<SessionFilterValue>('ALL');\n const pager = useOffsetPager(PAGE_SIZE);\n\n const sessions = useMybSessions({\n status: filter === 'ALL' ? undefined : filter,\n pagination: { limit: PAGE_SIZE, offset: pager.offset },\n });\n\n const programs = useMybPrograms({ status: 'ACTIVE', pagination: { limit: 50 } });\n const activePrograms = programs.data?.items ?? [];\n\n const startSession = useMybStartSession();\n\n const [startOpen, setStartOpen] = useState(false);\n const [form, setForm] = useState<StartForm>(EMPTY_START_FORM);\n const [formError, setFormError] = useState<string | null>(null);\n\n // Workouts for the selected program, so a session can be tied to a specific\n // planned workout — required for the guided next-exercise stepper on\n // SessionDetailPage (BOFF-4096) to have anything to walk through.\n const selectedProgramId = form.programId === NO_PROGRAM ? undefined : form.programId;\n const programWorkouts = useMybProgramWorkouts(selectedProgramId);\n const workoutOptions = programWorkouts.data ?? [];\n\n const rows = sessions.data?.items ?? [];\n const hasMore = sessions.data?.pageInfo?.hasMore ?? false;\n\n const onFilterChange = (value: SessionFilterValue) => {\n setFilter(value);\n pager.reset();\n };\n\n const openStart = () => {\n setForm(EMPTY_START_FORM);\n setFormError(null);\n setStartOpen(true);\n analytics.emitIntent('session', 'create', { workspaceId });\n };\n\n const submitStart = async () => {\n setFormError(null);\n try {\n const created = await startSession.mutateAsync({\n programId: selectedProgramId,\n workoutId: form.workoutId === NO_WORKOUT ? undefined : form.workoutId,\n scheduledFor: form.scheduledFor ? new Date(form.scheduledFor).toISOString() : undefined,\n notes: form.notes.trim() ? form.notes.trim() : undefined,\n });\n analytics.emitFact('session', 'started', { workspaceId, entityId: created.id });\n setStartOpen(false);\n navigateTo(`train/${created.id}`);\n } catch (e) {\n setFormError(e instanceof Error ? e.message : 'Failed to start the session.');\n }\n };\n\n return (\n <PageLayout\n title=\"Train\"\n description=\"Live workout sessions — start, log sets, and complete.\"\n actions={\n <Button onClick={openStart}>\n <Plus className=\"h-4 w-4\" /> Start session\n </Button>\n }\n >\n <div className=\"mb-6\">\n <FilterTabs options={STATUS_TABS} value={filter} onChange={onFilterChange} />\n </div>\n\n {sessions.isLoading ? (\n <LoadingView label=\"Loading your sessions…\" />\n ) : sessions.isError ? (\n <ErrorView\n message=\"We couldn't load your workout sessions.\"\n onRetry={() => sessions.refetch()}\n />\n ) : rows.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n icon={<Flame className=\"h-10 w-10\" />}\n title={filter === 'ALL' ? 'No sessions yet' : 'No sessions in this state'}\n description={\n filter === 'ALL'\n ? 'Start a workout session to begin logging sets and tracking your training.'\n : 'Try a different filter, or start a new workout session.'\n }\n action={\n <Button onClick={openStart}>\n <Plus className=\"h-4 w-4\" /> Start session\n </Button>\n }\n />\n ) : (\n <>\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n {rows.map((s, i) => {\n const meta = sessionMeta(s);\n const Icon = meta.icon;\n const isLive = s.status === 'IN_PROGRESS';\n const setCount = s.sets?.length;\n const rpe = s.perceivedExertion;\n return (\n <motion.button\n key={s.id}\n type=\"button\"\n onClick={() => navigateTo(`train/${s.id}`)}\n className=\"block w-full text-left\"\n initial={reduce ? false : { opacity: 0, y: 12 }}\n animate={{ opacity: 1, y: 0 }}\n transition={\n reduce\n ? { duration: 0 }\n : { duration: 0.35, delay: Math.min(i, 8) * 0.05, ease: MOTION_EASE }\n }\n whileHover={reduce ? undefined : { y: -3 }}\n whileTap={reduce ? undefined : { scale: 0.995 }}\n >\n <GlassCard\n treatment=\"plain\"\n glow={isLive}\n className={\n 'h-full p-4 transition-shadow hover:shadow-float' +\n (isLive\n ? ' ring-1 ring-inset ring-[color-mix(in_oklab,var(--color-action-primary-bg)_45%,transparent)]'\n : '')\n }\n >\n <div className=\"flex items-center justify-between gap-2\">\n <IconChip icon={<Icon className=\"h-4 w-4\" />} tint={meta.tint} />\n <StatusBadge status={s.status} />\n </div>\n <p className=\"mt-3 text-base font-semibold text-text-primary\">{meta.title}</p>\n <p className=\"mt-0.5 text-xs text-text-tertiary\">\n {meta.stampLabel} {formatRelativeTime(meta.stamp)}\n </p>\n\n <div className=\"mt-3 flex flex-wrap items-center gap-x-4 gap-y-1 text-xs text-text-secondary\">\n {s.caloriesBurned > 0 && (\n <span className=\"inline-flex items-center gap-1 tabular-nums\">\n <Flame className=\"h-3.5 w-3.5 text-status-warning-text\" />\n {formatKcal(s.caloriesBurned)}\n </span>\n )}\n {rpe != null && (\n <span className=\"inline-flex items-center gap-1 tabular-nums\">\n <Gauge className=\"h-3.5 w-3.5 text-accent\" />\n RPE {rpe}/10\n </span>\n )}\n {setCount != null && setCount > 0 && (\n <span className=\"inline-flex items-center gap-1 tabular-nums\">\n <ListChecks className=\"h-3.5 w-3.5 text-accent\" />\n {formatNumber(setCount)} sets\n </span>\n )}\n </div>\n\n {rpe != null && (\n <div className=\"mt-3\">\n <div className=\"mb-1 flex items-center justify-between text-[11px] text-text-tertiary\">\n <span>RPE intensity</span>\n <span className=\"tabular-nums\">{rpe}/10</span>\n </div>\n <Meter value={rpe * 10} height={6} />\n </div>\n )}\n\n {isLive && (\n <p className=\"mt-3 inline-flex items-center gap-1 text-xs font-semibold text-action-primary-bg\">\n In progress — Resume →\n </p>\n )}\n </GlassCard>\n </motion.button>\n );\n })}\n </div>\n\n <Pagination\n page={pager.page}\n pageSize={PAGE_SIZE}\n itemCount={rows.length}\n hasMore={hasMore}\n onPageChange={(t) => pager.onPageChange(t)}\n />\n </>\n )}\n\n {/* Start session dialog */}\n <ResponsiveDialog open={startOpen} onOpenChange={setStartOpen}>\n <ResponsiveDialogContent>\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>Start a workout session</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n Begin a live session now, or schedule one for later. Attach an active program to keep\n your training organised.\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n\n <div className=\"space-y-4 px-1\">\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-start-program\">Program (optional)</Label>\n <Select\n value={form.programId}\n onValueChange={(v) =>\n // Reset the workout choice too — the previous selection almost\n // certainly doesn't belong to the newly-picked program.\n setForm((f) => ({ ...f, programId: v, workoutId: NO_WORKOUT }))\n }\n >\n <SelectTrigger id=\"myb-start-program\">\n <SelectValue placeholder=\"No program\" />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value={NO_PROGRAM}>No program</SelectItem>\n {activePrograms.map((p) => (\n <SelectItem key={p.id} value={p.id}>\n {p.name}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n\n {selectedProgramId && workoutOptions.length > 0 && (\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-start-workout\">Workout (optional)</Label>\n <Select\n value={form.workoutId}\n onValueChange={(v) => setForm((f) => ({ ...f, workoutId: v }))}\n >\n <SelectTrigger id=\"myb-start-workout\">\n <SelectValue placeholder=\"No specific workout\" />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value={NO_WORKOUT}>No specific workout</SelectItem>\n {workoutOptions.map((w) => (\n <SelectItem key={w.id} value={w.id}>\n {w.name} — Week {w.weekNumber}, Day {w.dayOfWeek}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n <p className=\"text-xs text-text-tertiary\">\n Attach a planned workout to guide this session exercise-by-exercise.\n </p>\n </div>\n )}\n\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-start-scheduled\">Scheduled for (optional)</Label>\n <Input\n id=\"myb-start-scheduled\"\n type=\"datetime-local\"\n value={form.scheduledFor}\n onChange={(e) => setForm((f) => ({ ...f, scheduledFor: e.target.value }))}\n />\n <p className=\"text-xs text-text-tertiary\">\n Leave empty to start a live session right now.\n </p>\n </div>\n\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-start-notes\">Notes (optional)</Label>\n <Textarea\n id=\"myb-start-notes\"\n rows={3}\n value={form.notes}\n onChange={(e) => setForm((f) => ({ ...f, notes: e.target.value }))}\n placeholder=\"Focus, target lifts, how you're feeling…\"\n />\n </div>\n\n {formError && <p className=\"text-sm text-status-error-text\">{formError}</p>}\n </div>\n\n <ResponsiveDialogFooter>\n <Button\n variant=\"outline\"\n onClick={() => setStartOpen(false)}\n disabled={startSession.isPending}\n >\n Cancel\n </Button>\n <Button onClick={submitStart} disabled={startSession.isPending}>\n {startSession.isPending ? 'Starting…' : 'Start session'}\n </Button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@burdenoff/microfe-mybodyshield",
|
|
3
|
-
"version": "2026.
|
|
3
|
+
"version": "2026.731.1",
|
|
4
4
|
"description": "Burdenoff MyBodyShield Microfrontend — health & fitness (training, nutrition, progress, wellbeing)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
@@ -27,12 +27,12 @@
|
|
|
27
27
|
"scripts": {
|
|
28
28
|
"dev": "vite",
|
|
29
29
|
"predev": "bun run codegen",
|
|
30
|
-
"build": "tsc && vite build",
|
|
30
|
+
"build": "tsc && bash scripts/build-limits.sh --gate vite build",
|
|
31
31
|
"prebuild": "bun run codegen",
|
|
32
32
|
"lint": "eslint .",
|
|
33
33
|
"format": "prettier --write \"src/**/*.{ts,tsx}\"",
|
|
34
|
-
"type:check": "tsc --noEmit",
|
|
35
|
-
"sanity": "bun run codegen && bun run lint && bun run format && bun run type:check && bun run build && bun run build-storybook",
|
|
34
|
+
"type:check": "bash scripts/build-limits.sh tsc --noEmit",
|
|
35
|
+
"sanity": "bash scripts/build-limits.sh --gate bash -c 'bun run codegen && bun run lint && bun run format && bun run type:check && bun run build && bun run build-storybook'",
|
|
36
36
|
"storybook": "storybook dev -p 6006",
|
|
37
37
|
"build-storybook": "storybook build",
|
|
38
38
|
"test:storybook": "test-storybook",
|