@burdenoff/microfe-mybodyshield 2026.716.1 → 2026.720.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.
@@ -6,7 +6,26 @@ export interface ProgramFilters {
6
6
  pagination?: PaginationInput;
7
7
  }
8
8
  export declare const useMybPrograms: (filters?: ProgramFilters) => import('@tanstack/react-query').UseQueryResult<NoInfer<MybConnection<MybProgram>>, Error>;
9
- export declare const useMybProgram: (id: string | undefined) => import('@tanstack/react-query').UseQueryResult<NoInfer<MybProgram | null>, Error>;
9
+ export declare const useMybProgram: (id: string | undefined) => import('@tanstack/react-query').UseQueryResult<NoInfer<{
10
+ workouts: MybWorkout[];
11
+ status: MybProgramStatus;
12
+ memberId: string;
13
+ name: string;
14
+ description?: string | null | undefined;
15
+ coachId?: string | null | undefined;
16
+ goalType: import('../types').MybGoalType;
17
+ level: import('../types').MybFitnessLevel;
18
+ phase: import('../types').MybProgramPhase;
19
+ weeks: number;
20
+ daysPerWeek: number;
21
+ startDate?: string | null | undefined;
22
+ endDate?: string | null | undefined;
23
+ tags: string[];
24
+ workspaceId: string;
25
+ id: string;
26
+ createdAt: string;
27
+ updatedAt: string;
28
+ } | null>, Error>;
10
29
  export declare const useMybProgramWorkouts: (programId: string | undefined) => import('@tanstack/react-query').UseQueryResult<NoInfer<MybWorkout[]>, Error>;
11
30
  export declare const useMybCreateProgram: () => import('@tanstack/react-query').UseMutationResult<MybProgram, Error, MybCreateProgramInput, unknown>;
12
31
  export declare const useMybUpdateProgram: () => import('@tanstack/react-query').UseMutationResult<MybProgram, Error, {
@@ -29,7 +48,23 @@ export interface SessionFilters {
29
48
  pagination?: PaginationInput;
30
49
  }
31
50
  export declare const useMybSessions: (filters?: SessionFilters) => import('@tanstack/react-query').UseQueryResult<NoInfer<MybConnection<MybWorkoutSession>>, Error>;
32
- export declare const useMybSession: (id: string | undefined) => import('@tanstack/react-query').UseQueryResult<NoInfer<MybWorkoutSession | null>, Error>;
51
+ export declare const useMybSession: (id: string | undefined) => import('@tanstack/react-query').UseQueryResult<NoInfer<{
52
+ sets: MybSessionSet[];
53
+ status: MybSessionStatus;
54
+ memberId: string;
55
+ workspaceId: string;
56
+ id: string;
57
+ programId?: string | null | undefined;
58
+ createdAt: string;
59
+ updatedAt: string;
60
+ workoutId?: string | null | undefined;
61
+ scheduledFor?: string | null | undefined;
62
+ startedAt?: string | null | undefined;
63
+ completedAt?: string | null | undefined;
64
+ caloriesBurned: number;
65
+ perceivedExertion?: number | null | undefined;
66
+ notes?: string | null | undefined;
67
+ } | null>, Error>;
33
68
  export declare const useMybStartSession: () => import('@tanstack/react-query').UseMutationResult<MybWorkoutSession, Error, MybStartSessionInput, unknown>;
34
69
  export declare const useMybLogSet: () => import('@tanstack/react-query').UseMutationResult<MybSessionSet, Error, MybLogSetInput, unknown>;
35
70
  export declare const useMybCompleteSession: () => import('@tanstack/react-query').UseMutationResult<MybWorkoutSession, Error, {
@@ -72,9 +107,9 @@ export interface BodyMetricFilters {
72
107
  }
73
108
  export declare const useMybBodyMetrics: (filters?: BodyMetricFilters) => import('@tanstack/react-query').UseQueryResult<NoInfer<MybConnection<MybBodyMetric>>, Error>;
74
109
  export declare const useMybRecordBodyMetric: () => import('@tanstack/react-query').UseMutationResult<MybBodyMetric, Error, MybRecordMetricInput, unknown>;
75
- export declare const useMybRecoveryLogs: (metric?: MybRecoveryMetric, memberId?: string) => import('@tanstack/react-query').UseQueryResult<NoInfer<MybRecoveryLog[]>, Error>;
110
+ export declare const useMybRecoveryLogs: (metric?: MybRecoveryMetric, memberId?: string) => import('@tanstack/react-query').UseQueryResult<NoInfer<MybConnection<MybRecoveryLog>>, Error>;
76
111
  export declare const useMybLogRecovery: () => import('@tanstack/react-query').UseMutationResult<MybRecoveryLog, Error, MybLogRecoveryInput, unknown>;
77
- export declare const useMybCoaching: (status?: MybCoachingStatus, memberId?: string) => import('@tanstack/react-query').UseQueryResult<NoInfer<MybCoachingRelationship[]>, Error>;
112
+ export declare const useMybCoaching: (status?: MybCoachingStatus, memberId?: string) => import('@tanstack/react-query').UseQueryResult<NoInfer<MybConnection<MybCoachingRelationship>>, Error>;
78
113
  export declare const useMybCreateCoaching: () => import('@tanstack/react-query').UseMutationResult<MybCoachingRelationship, Error, MybCreateCoachingInput, unknown>;
79
114
  export declare const useMybTransitionCoaching: () => import('@tanstack/react-query').UseMutationResult<MybCoachingRelationship, Error, {
80
115
  id: string;
@@ -1 +1 @@
1
- {"version":3,"file":"useMyBodyShieldApi.d.ts","sourceRoot":"","sources":["../../../src/mybodyshield/hooks/useMyBodyShieldApi.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"useMyBodyShieldApi.d.ts","sourceRoot":"","sources":["../../../src/mybodyshield/hooks/useMyBodyShieldApi.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAMH,OAAO,KAAK,EACV,aAAa,EACb,UAAU,EACV,gBAAgB,EAChB,UAAU,EAEV,iBAAiB,EACjB,gBAAgB,EAChB,aAAa,EACb,OAAO,EACP,aAAa,EACb,UAAU,EACV,WAAW,EACX,gBAAgB,EAChB,cAAc,EACd,aAAa,EACb,iBAAiB,EACjB,cAAc,EACd,iBAAiB,EACjB,uBAAuB,EACvB,iBAAiB,EACjB,qBAAqB,EACrB,qBAAqB,EACrB,qBAAqB,EACrB,sBAAsB,EACtB,oBAAoB,EACpB,cAAc,EACd,uBAAuB,EACvB,kBAAkB,EAClB,kBAAkB,EAClB,2BAA2B,EAC3B,2BAA2B,EAC3B,eAAe,EACf,oBAAoB,EACpB,mBAAmB,EACnB,sBAAsB,EACtB,eAAe,EAChB,MAAM,UAAU,CAAC;AAsJlB,MAAM,WAAW,cAAc;IAC7B,MAAM,CAAC,EAAE,gBAAgB,CAAC;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,eAAe,CAAC;CAC9B;AAED,eAAO,MAAM,cAAc,GAAI,UAAS,cAAmB,8FAsB1D,CAAC;AAEF,eAAO,MAAM,aAAa,GAAI,IAAI,MAAM,GAAG,SAAS;;;;;;;;;;;;;;;;;;;iBA8BnD,CAAC;AAEF,eAAO,MAAM,qBAAqB,GAAI,WAAW,MAAM,GAAG,SAAS,iFAuBlE,CAAC;AAEF,eAAO,MAAM,mBAAmB,4GAgB/B,CAAC;AAEF,eAAO,MAAM,mBAAmB;QAIG,MAAM;WAAS,qBAAqB;WAetE,CAAC;AAEF,eAAO,MAAM,uBAAuB;QAID,MAAM;YAAU,gBAAgB;WAelE,CAAC;AAEF,eAAO,MAAM,oBAAoB,0FAchC,CAAC;AAEF,eAAO,MAAM,gBAAgB,4GAmB5B,CAAC;AAEF,eAAO,MAAM,iBAAiB;QAK8B,MAAM;eAAa,MAAM;0CAgBpF,CAAC;AAMF,MAAM,WAAW,cAAc;IAC7B,MAAM,CAAC,EAAE,gBAAgB,CAAC;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,eAAe,CAAC;CAC9B;AAED,eAAO,MAAM,cAAc,GAAI,UAAS,cAAmB,qGAsB1D,CAAC;AAEF,eAAO,MAAM,aAAa,GAAI,IAAI,MAAM,GAAG,SAAS;;;;;;;;;;;;;;;;iBAuBnD,CAAC;AAEF,eAAO,MAAM,kBAAkB,kHAgB9B,CAAC;AAEF,eAAO,MAAM,YAAY,wGAmBxB,CAAC;AAEF,eAAO,MAAM,qBAAqB;QAIC,MAAM;WAAS,uBAAuB;WAexE,CAAC;AAMF,MAAM,WAAW,WAAW;IAC1B,MAAM,CAAC,EAAE,aAAa,CAAC;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,eAAe,CAAC;CAC9B;AAED,eAAO,MAAM,WAAW,GAAI,UAAS,WAAgB,2FAsBpD,CAAC;AAEF,eAAO,MAAM,gBAAgB,sGAgB5B,CAAC;AAEF,eAAO,MAAM,gBAAgB;QAIM,MAAM;WAAS,kBAAkB;WAYnE,CAAC;AAEF,eAAO,MAAM,iBAAiB,0FAgB7B,CAAC;AAMF,eAAO,MAAM,yBAAyB,GAAI,WAAW,MAAM,4FAe1D,CAAC;AAEF,eAAO,MAAM,mBAAmB,GAAI,IAAI,MAAM,GAAG,SAAS,4FAmBzD,CAAC;AAEF,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,EAAE,WAAW,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,eAAe,CAAC;CAC9B;AAED,eAAO,MAAM,WAAW,GAAI,UAAS,WAAgB,8FAsBpD,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAAI,OAAO,MAAM,EAAE,WAAW,MAAM,mFAejE,CAAC;AAEF,eAAO,MAAM,yBAAyB,wHAkBrC,CAAC;AAEF,eAAO,MAAM,yBAAyB;QAIH,MAAM;WAAS,2BAA2B;WAe5E,CAAC;AAEF,eAAO,MAAM,2BAA2B,mGAmBvC,CAAC;AAEF,eAAO,MAAM,aAAa,sGAoBzB,CAAC;AAEF,eAAO,MAAM,gBAAgB,0FAiB5B,CAAC;AAMF,MAAM,WAAW,iBAAiB;IAChC,IAAI,CAAC,EAAE,iBAAiB,CAAC;IACzB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,eAAe,CAAC;CAC9B;AAED,eAAO,MAAM,iBAAiB,GAAI,UAAS,iBAAsB,iGAsBhE,CAAC;AAEF,eAAO,MAAM,sBAAsB,8GAgBlC,CAAC;AAMF,eAAO,MAAM,kBAAkB,GAAI,SAAS,iBAAiB,EAAE,WAAW,MAAM,kGAkB/E,CAAC;AAEF,eAAO,MAAM,iBAAiB,8GAgB7B,CAAC;AAMF,eAAO,MAAM,cAAc,GAAI,SAAS,iBAAiB,EAAE,WAAW,MAAM,2GAkB3E,CAAC;AAEF,eAAO,MAAM,oBAAoB,0HAgBhC,CAAC;AAEF,eAAO,MAAM,wBAAwB;QAIF,MAAM;YAAU,iBAAiB;WAYnE,CAAC"}
@@ -52,7 +52,10 @@ var a = () => {
52
52
  bodyMetrics: ["mybodyshield", "body-metrics"],
53
53
  recovery: ["mybodyshield", "recovery"],
54
54
  coaching: ["mybodyshield", "coaching"]
55
- }, c = { limit: 50 }, l = "\n id workspaceId memberId coachId name description goalType level phase status\n weeks daysPerWeek startDate endDate tags createdAt updatedAt\n", u = "\n id workspaceId workoutId name instructions muscleGroup sets reps targetWeight\n restSeconds orderIndex demo { id } createdAt updatedAt\n", d = "\n id workspaceId programId name description type dayOfWeek weekNumber orderIndex\n durationMin createdAt updatedAt\n", f = "\n id workspaceId sessionId exerciseName setNumber reps weight distanceM durationSec\n isPersonalRecord createdAt updatedAt\n", p = "\n id workspaceId memberId programId workoutId status scheduledFor startedAt completedAt\n caloriesBurned perceivedExertion notes createdAt updatedAt\n", m = "\n id workspaceId memberId title description type status targetValue currentValue\n baselineValue unit targetDate achievedAt createdAt updatedAt\n", h = "\n id workspaceId memberId planId name mealType calories proteinG carbsG fatG servings\n isFavorite loggedAt createdAt updatedAt\n", g = "\n id workspaceId memberId coachId name goalType calorieTarget proteinTarget carbsTarget\n fatTarget startDate endDate isActive notes createdAt updatedAt\n", _ = "\n id workspaceId memberId type value unit measuredAt notes photo { id } createdAt updatedAt\n", v = "\n id workspaceId memberId metric score sleepHours notes loggedAt createdAt updatedAt\n", y = "\n id workspaceId coachId memberId status specialty shareWorkouts shareNutrition\n shareMetrics startedAt endedAt notes createdAt updatedAt\n", b = "\n date calories proteinG carbsG fatG calorieTarget proteinTarget carbsTarget fatTarget\n", x = (t = {}) => {
55
+ }, c = { limit: 50 }, l = "\n id workspaceId memberId coachId name description goalType level phase status\n weeks daysPerWeek startDate endDate tags createdAt updatedAt\n", u = "\n id workspaceId workoutId name instructions muscleGroup sets reps targetWeight\n restSeconds orderIndex demo { id } createdAt updatedAt\n", d = "\n id workspaceId programId name description type dayOfWeek weekNumber orderIndex\n durationMin createdAt updatedAt\n", f = "\n id workspaceId sessionId exerciseName setNumber reps weight distanceM durationSec\n isPersonalRecord createdAt updatedAt\n", p = "\n id workspaceId memberId programId workoutId status scheduledFor startedAt completedAt\n caloriesBurned perceivedExertion notes createdAt updatedAt\n", m = "\n id workspaceId memberId title description type status targetValue currentValue\n baselineValue unit targetDate achievedAt createdAt updatedAt\n", h = "\n id workspaceId memberId planId name mealType calories proteinG carbsG fatG servings\n isFavorite loggedAt createdAt updatedAt\n", g = "\n id workspaceId memberId coachId name goalType calorieTarget proteinTarget carbsTarget\n fatTarget startDate endDate isActive notes createdAt updatedAt\n", _ = "\n id workspaceId memberId type value unit measuredAt notes photo { id } createdAt updatedAt\n", v = "\n id workspaceId memberId metric score sleepHours notes loggedAt createdAt updatedAt\n", y = "\n id workspaceId coachId memberId status specialty shareWorkouts shareNutrition\n shareMetrics startedAt endedAt notes createdAt updatedAt\n", b = "\n date calories proteinG carbsG fatG calorieTarget proteinTarget carbsTarget fatTarget\n", x = (e) => e.map(({ exercises: e, ...t }) => ({
56
+ ...t,
57
+ exercises: e.items
58
+ })), S = (t = {}) => {
56
59
  let { apiGatewayUrl: r } = e();
57
60
  return n({
58
61
  queryKey: [...s.programs, t],
@@ -67,22 +70,32 @@ var a = () => {
67
70
  pagination: t.pagination ?? c
68
71
  }, r)).mybListPrograms
69
72
  });
70
- }, S = (t) => {
73
+ }, C = (t) => {
71
74
  let { apiGatewayUrl: r } = e();
72
75
  return n({
73
76
  queryKey: s.program(t ?? ""),
74
77
  enabled: !!t,
75
- queryFn: async () => (await o(`query MybProgram($id: ID!) {
78
+ queryFn: async () => {
79
+ let e = await o(`query MybProgram($id: ID!) {
76
80
  mybProgram(id: $id) {
77
81
  ${l}
78
82
  workouts {
79
- ${d}
80
- exercises { ${u} }
83
+ items {
84
+ ${d}
85
+ exercises { items { ${u} } }
86
+ }
81
87
  }
82
88
  }
83
- }`, { id: t }, r)).mybProgram
89
+ }`, { id: t }, r);
90
+ if (!e.mybProgram) return null;
91
+ let { workouts: n, ...i } = e.mybProgram;
92
+ return {
93
+ ...i,
94
+ workouts: x(n.items)
95
+ };
96
+ }
84
97
  });
85
- }, C = () => {
98
+ }, w = () => {
86
99
  let { apiGatewayUrl: n } = e(), i = r();
87
100
  return t({
88
101
  mutationFn: async (e) => (await o(`mutation MybCreateProgram($input: MybCreateProgramInput!) {
@@ -90,7 +103,7 @@ var a = () => {
90
103
  }`, { input: e }, n)).mybCreateProgram,
91
104
  onSuccess: () => i.invalidateQueries({ queryKey: s.programs })
92
105
  });
93
- }, w = () => {
106
+ }, T = () => {
94
107
  let { apiGatewayUrl: n } = e(), i = r();
95
108
  return t({
96
109
  mutationFn: async (e) => (await o(`mutation MybUpdateProgram($id: ID!, $input: MybUpdateProgramInput!) {
@@ -100,7 +113,7 @@ var a = () => {
100
113
  i.invalidateQueries({ queryKey: s.programs }), i.invalidateQueries({ queryKey: s.program(e.id) });
101
114
  }
102
115
  });
103
- }, T = () => {
116
+ }, E = () => {
104
117
  let { apiGatewayUrl: n } = e(), i = r();
105
118
  return t({
106
119
  mutationFn: async (e) => (await o(`mutation MybTransitionProgram($id: ID!, $status: MybProgramStatus!) {
@@ -110,13 +123,13 @@ var a = () => {
110
123
  i.invalidateQueries({ queryKey: s.programs }), i.invalidateQueries({ queryKey: s.program(e.id) });
111
124
  }
112
125
  });
113
- }, E = () => {
126
+ }, D = () => {
114
127
  let { apiGatewayUrl: n } = e(), i = r();
115
128
  return t({
116
129
  mutationFn: async (e) => (await o("mutation MybArchiveProgram($id: ID!) { mybArchiveProgram(id: $id) }", { id: e }, n)).mybArchiveProgram,
117
130
  onSuccess: () => i.invalidateQueries({ queryKey: s.programs })
118
131
  });
119
- }, D = () => {
132
+ }, O = () => {
120
133
  let { apiGatewayUrl: n } = e(), i = r();
121
134
  return t({
122
135
  mutationFn: async (e) => (await o(`mutation MybAddWorkout($input: MybCreateWorkoutInput!) {
@@ -126,7 +139,7 @@ var a = () => {
126
139
  i.invalidateQueries({ queryKey: s.program(e.programId) }), i.invalidateQueries({ queryKey: s.programWorkouts(e.programId) });
127
140
  }
128
141
  });
129
- }, O = () => {
142
+ }, k = () => {
130
143
  let { apiGatewayUrl: n } = e(), i = r();
131
144
  return t({
132
145
  mutationFn: async (e) => (await o("mutation MybAddExercise($input: MybCreateExerciseInput!) {\n mybAddExercise(input: $input) { id workoutId }\n }", { input: e }, n)).mybAddExercise,
@@ -134,7 +147,7 @@ var a = () => {
134
147
  i.invalidateQueries({ queryKey: ["mybodyshield", "program"] }), i.invalidateQueries({ queryKey: ["mybodyshield", "program-workouts"] });
135
148
  }
136
149
  });
137
- }, k = (t = {}) => {
150
+ }, A = (t = {}) => {
138
151
  let { apiGatewayUrl: r } = e();
139
152
  return n({
140
153
  queryKey: [...s.sessions, t],
@@ -149,19 +162,27 @@ var a = () => {
149
162
  pagination: t.pagination ?? c
150
163
  }, r)).mybListSessions
151
164
  });
152
- }, A = (t) => {
165
+ }, j = (t) => {
153
166
  let { apiGatewayUrl: r } = e();
154
167
  return n({
155
168
  queryKey: s.session(t ?? ""),
156
169
  enabled: !!t,
157
- queryFn: async () => (await o(`query MybSession($id: ID!) {
170
+ queryFn: async () => {
171
+ let e = await o(`query MybSession($id: ID!) {
158
172
  mybSession(id: $id) {
159
173
  ${p}
160
- sets { ${f} }
174
+ sets { items { ${f} } }
161
175
  }
162
- }`, { id: t }, r)).mybSession
176
+ }`, { id: t }, r);
177
+ if (!e.mybSession) return null;
178
+ let { sets: n, ...i } = e.mybSession;
179
+ return {
180
+ ...i,
181
+ sets: n.items
182
+ };
183
+ }
163
184
  });
164
- }, j = () => {
185
+ }, M = () => {
165
186
  let { apiGatewayUrl: n } = e(), i = r();
166
187
  return t({
167
188
  mutationFn: async (e) => (await o(`mutation MybStartSession($input: MybStartSessionInput!) {
@@ -169,7 +190,7 @@ var a = () => {
169
190
  }`, { input: e }, n)).mybStartSession,
170
191
  onSuccess: () => i.invalidateQueries({ queryKey: s.sessions })
171
192
  });
172
- }, M = () => {
193
+ }, N = () => {
173
194
  let { apiGatewayUrl: n } = e(), i = r();
174
195
  return t({
175
196
  mutationFn: async (e) => (await o(`mutation MybLogSet($input: MybLogSetInput!) {
@@ -179,7 +200,7 @@ var a = () => {
179
200
  i.invalidateQueries({ queryKey: s.session(e.sessionId) }), i.invalidateQueries({ queryKey: s.sessions });
180
201
  }
181
202
  });
182
- }, N = () => {
203
+ }, P = () => {
183
204
  let { apiGatewayUrl: n } = e(), i = r();
184
205
  return t({
185
206
  mutationFn: async (e) => (await o(`mutation MybCompleteSession($id: ID!, $input: MybCompleteSessionInput!) {
@@ -189,7 +210,7 @@ var a = () => {
189
210
  i.invalidateQueries({ queryKey: s.session(e.id) }), i.invalidateQueries({ queryKey: s.sessions });
190
211
  }
191
212
  });
192
- }, P = (t = {}) => {
213
+ }, F = (t = {}) => {
193
214
  let { apiGatewayUrl: r } = e();
194
215
  return n({
195
216
  queryKey: [...s.goals, t],
@@ -204,7 +225,7 @@ var a = () => {
204
225
  pagination: t.pagination ?? c
205
226
  }, r)).mybListGoals
206
227
  });
207
- }, F = () => {
228
+ }, I = () => {
208
229
  let { apiGatewayUrl: n } = e(), i = r();
209
230
  return t({
210
231
  mutationFn: async (e) => (await o(`mutation MybCreateGoal($input: MybCreateGoalInput!) {
@@ -212,7 +233,7 @@ var a = () => {
212
233
  }`, { input: e }, n)).mybCreateGoal,
213
234
  onSuccess: () => i.invalidateQueries({ queryKey: s.goals })
214
235
  });
215
- }, I = () => {
236
+ }, L = () => {
216
237
  let { apiGatewayUrl: n } = e(), i = r();
217
238
  return t({
218
239
  mutationFn: async (e) => (await o(`mutation MybUpdateGoal($id: ID!, $input: MybUpdateGoalInput!) {
@@ -220,7 +241,7 @@ var a = () => {
220
241
  }`, e, n)).mybUpdateGoal,
221
242
  onSuccess: () => i.invalidateQueries({ queryKey: s.goals })
222
243
  });
223
- }, L = () => {
244
+ }, R = () => {
224
245
  let { apiGatewayUrl: n } = e(), i = r();
225
246
  return t({
226
247
  mutationFn: async (e) => (await o(`mutation MybAchieveGoal($id: ID!) {
@@ -228,7 +249,7 @@ var a = () => {
228
249
  }`, { id: e }, n)).mybAchieveGoal,
229
250
  onSuccess: () => i.invalidateQueries({ queryKey: s.goals })
230
251
  });
231
- }, R = (t) => {
252
+ }, z = (t) => {
232
253
  let { apiGatewayUrl: r } = e();
233
254
  return n({
234
255
  queryKey: [...s.activePlan, t ?? "me"],
@@ -236,7 +257,7 @@ var a = () => {
236
257
  mybActiveNutritionPlan(memberId: $memberId) { ${g} }
237
258
  }`, { memberId: t }, r)).mybActiveNutritionPlan
238
259
  });
239
- }, z = (t = {}) => {
260
+ }, B = (t = {}) => {
240
261
  let { apiGatewayUrl: r } = e();
241
262
  return n({
242
263
  queryKey: [...s.meals, t],
@@ -251,7 +272,7 @@ var a = () => {
251
272
  pagination: t.pagination ?? c
252
273
  }, r)).mybListMeals
253
274
  });
254
- }, B = (t, r) => {
275
+ }, V = (t, r) => {
255
276
  let { apiGatewayUrl: i } = e();
256
277
  return n({
257
278
  queryKey: [
@@ -266,7 +287,7 @@ var a = () => {
266
287
  memberId: r
267
288
  }, i)).mybDailyMacros
268
289
  });
269
- }, V = () => {
290
+ }, H = () => {
270
291
  let { apiGatewayUrl: n } = e(), i = r();
271
292
  return t({
272
293
  mutationFn: async (e) => (await o(`mutation MybCreateNutritionPlan($input: MybCreateNutritionPlanInput!) {
@@ -276,7 +297,7 @@ var a = () => {
276
297
  i.invalidateQueries({ queryKey: s.activePlan });
277
298
  }
278
299
  });
279
- }, H = () => {
300
+ }, U = () => {
280
301
  let { apiGatewayUrl: n } = e(), i = r();
281
302
  return t({
282
303
  mutationFn: async (e) => (await o(`mutation MybUpdateNutritionPlan($id: ID!, $input: MybUpdateNutritionPlanInput!) {
@@ -286,7 +307,7 @@ var a = () => {
286
307
  i.invalidateQueries({ queryKey: s.activePlan }), i.invalidateQueries({ queryKey: s.nutritionPlan(e.id) });
287
308
  }
288
309
  });
289
- }, U = () => {
310
+ }, W = () => {
290
311
  let { apiGatewayUrl: n } = e(), i = r();
291
312
  return t({
292
313
  mutationFn: async (e) => (await o(`mutation MybActivateNutritionPlan($id: ID!) {
@@ -296,7 +317,7 @@ var a = () => {
296
317
  i.invalidateQueries({ queryKey: s.activePlan }), i.invalidateQueries({ queryKey: s.nutritionPlan(e.id) });
297
318
  }
298
319
  });
299
- }, W = () => {
320
+ }, G = () => {
300
321
  let { apiGatewayUrl: n } = e(), i = r();
301
322
  return t({
302
323
  mutationFn: async (e) => (await o(`mutation MybLogMeal($input: MybLogMealInput!) {
@@ -306,7 +327,7 @@ var a = () => {
306
327
  i.invalidateQueries({ queryKey: s.meals }), i.invalidateQueries({ queryKey: s.dailyMacros }), e.planId && i.invalidateQueries({ queryKey: s.nutritionPlan(e.planId) });
307
328
  }
308
329
  });
309
- }, G = () => {
330
+ }, K = () => {
310
331
  let { apiGatewayUrl: n } = e(), i = r();
311
332
  return t({
312
333
  mutationFn: async (e) => (await o("mutation MybDeleteMeal($id: ID!) { mybDeleteMeal(id: $id) }", { id: e }, n)).mybDeleteMeal,
@@ -314,7 +335,7 @@ var a = () => {
314
335
  i.invalidateQueries({ queryKey: s.meals }), i.invalidateQueries({ queryKey: s.dailyMacros });
315
336
  }
316
337
  });
317
- }, K = (t = {}) => {
338
+ }, q = (t = {}) => {
318
339
  let { apiGatewayUrl: r } = e();
319
340
  return n({
320
341
  queryKey: [...s.bodyMetrics, t],
@@ -329,7 +350,7 @@ var a = () => {
329
350
  pagination: t.pagination ?? c
330
351
  }, r)).mybListBodyMetrics
331
352
  });
332
- }, q = () => {
353
+ }, J = () => {
333
354
  let { apiGatewayUrl: n } = e(), i = r();
334
355
  return t({
335
356
  mutationFn: async (e) => (await o(`mutation MybRecordBodyMetric($input: MybRecordMetricInput!) {
@@ -337,7 +358,7 @@ var a = () => {
337
358
  }`, { input: e }, n)).mybRecordBodyMetric,
338
359
  onSuccess: () => i.invalidateQueries({ queryKey: s.bodyMetrics })
339
360
  });
340
- }, J = (t, r) => {
361
+ }, Y = (t, r) => {
341
362
  let { apiGatewayUrl: i } = e();
342
363
  return n({
343
364
  queryKey: [
@@ -346,13 +367,16 @@ var a = () => {
346
367
  r ?? "me"
347
368
  ],
348
369
  queryFn: async () => (await o(`query MybListRecoveryLogs($metric: MybRecoveryMetric, $memberId: ID) {
349
- mybListRecoveryLogs(metric: $metric, memberId: $memberId) { ${v} }
370
+ mybListRecoveryLogs(metric: $metric, memberId: $memberId) {
371
+ items { ${v} }
372
+ pageInfo { cursor hasMore }
373
+ }
350
374
  }`, {
351
375
  metric: t,
352
376
  memberId: r
353
377
  }, i)).mybListRecoveryLogs
354
378
  });
355
- }, Y = () => {
379
+ }, X = () => {
356
380
  let { apiGatewayUrl: n } = e(), i = r();
357
381
  return t({
358
382
  mutationFn: async (e) => (await o(`mutation MybLogRecovery($input: MybLogRecoveryInput!) {
@@ -360,7 +384,7 @@ var a = () => {
360
384
  }`, { input: e }, n)).mybLogRecovery,
361
385
  onSuccess: () => i.invalidateQueries({ queryKey: s.recovery })
362
386
  });
363
- }, X = (t, r) => {
387
+ }, Z = (t, r) => {
364
388
  let { apiGatewayUrl: i } = e();
365
389
  return n({
366
390
  queryKey: [
@@ -369,13 +393,16 @@ var a = () => {
369
393
  r ?? "me"
370
394
  ],
371
395
  queryFn: async () => (await o(`query MybListCoaching($status: MybCoachingStatus, $memberId: ID) {
372
- mybListCoaching(status: $status, memberId: $memberId) { ${y} }
396
+ mybListCoaching(status: $status, memberId: $memberId) {
397
+ items { ${y} }
398
+ pageInfo { cursor hasMore }
399
+ }
373
400
  }`, {
374
401
  status: t,
375
402
  memberId: r
376
403
  }, i)).mybListCoaching
377
404
  });
378
- }, Z = () => {
405
+ }, Q = () => {
379
406
  let { apiGatewayUrl: n } = e(), i = r();
380
407
  return t({
381
408
  mutationFn: async (e) => (await o(`mutation MybCreateCoaching($input: MybCreateCoachingInput!) {
@@ -383,7 +410,7 @@ var a = () => {
383
410
  }`, { input: e }, n)).mybCreateCoaching,
384
411
  onSuccess: () => i.invalidateQueries({ queryKey: s.coaching })
385
412
  });
386
- }, Q = () => {
413
+ }, $ = () => {
387
414
  let { apiGatewayUrl: n } = e(), i = r();
388
415
  return t({
389
416
  mutationFn: async (e) => (await o(`mutation MybTransitionCoaching($id: ID!, $status: MybCoachingStatus!) {
@@ -393,6 +420,6 @@ var a = () => {
393
420
  });
394
421
  };
395
422
  //#endregion
396
- export { L as useMybAchieveGoal, U as useMybActivateNutritionPlan, R as useMybActiveNutritionPlan, O as useMybAddExercise, D as useMybAddWorkout, E as useMybArchiveProgram, K as useMybBodyMetrics, X as useMybCoaching, N as useMybCompleteSession, Z as useMybCreateCoaching, F as useMybCreateGoal, V as useMybCreateNutritionPlan, C as useMybCreateProgram, B as useMybDailyMacros, G as useMybDeleteMeal, P as useMybGoals, W as useMybLogMeal, Y as useMybLogRecovery, M as useMybLogSet, z as useMybMeals, S as useMybProgram, x as useMybPrograms, q as useMybRecordBodyMetric, J as useMybRecoveryLogs, A as useMybSession, k as useMybSessions, j as useMybStartSession, Q as useMybTransitionCoaching, T as useMybTransitionProgram, I as useMybUpdateGoal, H as useMybUpdateNutritionPlan, w as useMybUpdateProgram };
423
+ export { R as useMybAchieveGoal, W as useMybActivateNutritionPlan, z as useMybActiveNutritionPlan, k as useMybAddExercise, O as useMybAddWorkout, D as useMybArchiveProgram, q as useMybBodyMetrics, Z as useMybCoaching, P as useMybCompleteSession, Q as useMybCreateCoaching, I as useMybCreateGoal, H as useMybCreateNutritionPlan, w as useMybCreateProgram, V as useMybDailyMacros, K as useMybDeleteMeal, F as useMybGoals, G as useMybLogMeal, X as useMybLogRecovery, N as useMybLogSet, B as useMybMeals, C as useMybProgram, S as useMybPrograms, J as useMybRecordBodyMetric, Y as useMybRecoveryLogs, j as useMybSession, A as useMybSessions, M as useMybStartSession, $ as useMybTransitionCoaching, E as useMybTransitionProgram, L as useMybUpdateGoal, U as useMybUpdateNutritionPlan, T as useMybUpdateProgram };
397
424
 
398
425
  //# 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 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 sessions: ['mybodyshield', 'sessions'] as const,\n session: (id: string) => ['mybodyshield', 'session', id] 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// 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<{ mybProgram: MybProgram | null }>(\n `query MybProgram($id: ID!) {\n mybProgram(id: $id) {\n ${PROGRAM_FIELDS}\n workouts {\n ${WORKOUT_FIELDS}\n exercises { ${EXERCISE_FIELDS} }\n }\n }\n }`,\n { id },\n apiGatewayUrl\n );\n return result.mybProgram;\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<{ mybProgramWorkouts: MybWorkout[] }>(\n `query MybProgramWorkouts($programId: ID!) {\n mybProgramWorkouts(programId: $programId) {\n ${WORKOUT_FIELDS}\n exercises { ${EXERCISE_FIELDS} }\n }\n }`,\n { programId },\n apiGatewayUrl\n );\n return result.mybProgramWorkouts;\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<{ mybSession: MybWorkoutSession | null }>(\n `query MybSession($id: ID!) {\n mybSession(id: $id) {\n ${SESSION_FIELDS}\n sets { ${SET_FIELDS} }\n }\n }`,\n { id },\n apiGatewayUrl\n );\n return result.mybSession;\n },\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 },\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: MybRecoveryLog[] }>(\n `query MybListRecoveryLogs($metric: MybRecoveryMetric, $memberId: ID) {\n mybListRecoveryLogs(metric: $metric, memberId: $memberId) { ${RECOVERY_FIELDS} }\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: MybCoachingRelationship[] }>(\n `query MybListCoaching($status: MybCoachingStatus, $memberId: ID) {\n mybListCoaching(status: $status, memberId: $memberId) { ${COACHING_FIELDS} }\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 sessions: ['mybodyshield', 'sessions'] as const,\n session: (id: string) => ['mybodyshield', 'session', id] 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'> & { workouts: { items: MybWorkoutWithExerciseConnection[] } })\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\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\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 },\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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"}
@@ -66,7 +66,7 @@ function j() {
66
66
  }), B = ee(), V = p({
67
67
  type: "WEIGHT",
68
68
  pagination: { limit: 30 }
69
- }), fe = re("READINESS"), pe = L.data?.items ?? [], H = R.data?.items ?? [], U = z.data?.items ?? [], W = V.data?.items ?? [], me = fe.data ?? [], he = (/* @__PURE__ */ new Date()).getTime() - 10080 * 60 * 1e3, G = H.filter((e) => {
69
+ }), fe = re("READINESS"), pe = L.data?.items ?? [], H = R.data?.items ?? [], U = z.data?.items ?? [], W = V.data?.items ?? [], me = fe.data?.items ?? [], he = (/* @__PURE__ */ new Date()).getTime() - 10080 * 60 * 1e3, G = H.filter((e) => {
70
70
  let t = e.completedAt ?? e.startedAt ?? e.scheduledFor ?? e.createdAt;
71
71
  return t ? new Date(t).getTime() >= he : !1;
72
72
  }), ge = G.filter((e) => e.status === "COMPLETED").length, K = H.find((e) => e.status === "IN_PROGRESS"), q = H.filter((e) => e.status === "SCHEDULED" && e.scheduledFor).sort((e, t) => new Date(e.scheduledFor).getTime() - new Date(t.scheduledFor).getTime())[0], J = W[0], Y = W[1], X = J && Y ? J.value - Y.value : void 0, Z = me.slice().sort((e, t) => new Date(t.loggedAt).getTime() - new Date(e.loggedAt).getTime())[0], Q = b(() => U.length === 0 ? 0 : U.reduce((e, t) => e + y(t), 0) / U.length, [U]), $ = B.data, _e = $?.calorieTarget ? $.calories / $.calorieTarget * 100 : 0;
@@ -1 +1 @@
1
- {"version":3,"file":"DashboardPage.js","names":[],"sources":["../../../src/mybodyshield/pages/DashboardPage.tsx"],"sourcesContent":["/**\n * Dashboard — MyBodyShield health & fitness overview.\n * @module mybodyshield/pages\n *\n * KPIs are aggregated client-side from the real `myb*` list endpoints:\n * active programs, this-week's sessions, goal progress, today's macros vs\n * target, latest body weight and recovery readiness. No mock data.\n */\n\nimport { useMemo } from 'react';\nimport type { ReactNode } from 'react';\nimport { motion, useReducedMotion } from 'framer-motion';\nimport {\n Dumbbell,\n Flame,\n Target,\n Salad,\n Moon,\n ArrowRight,\n CheckCircle2,\n Weight,\n CalendarCheck,\n} from 'lucide-react';\nimport { GlassCard } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../components/PageLayout';\nimport { MetricsCard } from '../components/MetricsCard';\nimport { StatusBadge } from '../components/StatusBadge';\nimport { MiniBar } from '../components/MiniBar';\nimport { LoadingView, Section } from '../components/StateViews';\nimport { EmptyState } from '../components/EmptyState';\nimport {\n Meter,\n ProgressRing,\n MacroBar,\n MacroLegend,\n IconChip,\n GOAL_TYPE_META,\n RECOVERY_META,\n WORKOUT_TYPE_META,\n computeGoalProgress,\n} from './_shared/visuals';\nimport { useMyBodyShield } from '../context';\nimport {\n useMybPrograms,\n useMybSessions,\n useMybGoals,\n useMybDailyMacros,\n useMybBodyMetrics,\n useMybRecoveryLogs,\n} from '../hooks';\n\nimport {\n formatNumber,\n formatRelativeTime,\n formatKcal,\n formatWeight,\n formatPercent,\n formatEnumLabel,\n} from '../utils/formatters';\n\n/** Shared easing for entrance / hover motion (smooth ease-out). */\nconst EASE_OUT: [number, number, number, number] = [0.16, 1, 0.3, 1];\n\nexport function DashboardPage() {\n const { navigateTo, currentUser } = useMyBodyShield();\n const reduceMotion = useReducedMotion();\n\n /** Staggered fade/rise entrance — one-shot, skipped under reduced motion. */\n const rise = (i: number) =>\n reduceMotion\n ? {}\n : {\n initial: { opacity: 0, y: 14 },\n whileInView: { opacity: 1, y: 0 },\n viewport: { once: true, amount: 0.15 },\n transition: { duration: 0.45, delay: Math.min(i, 8) * 0.05, ease: EASE_OUT },\n };\n\n /** Gentle hover lift for cards. */\n const lift = reduceMotion\n ? {}\n : { whileHover: { y: -3, transition: { duration: 0.2, ease: EASE_OUT } } };\n\n /** Hover lift + tap feedback for interactive cards. */\n const liftTap = reduceMotion\n ? {}\n : {\n whileHover: { y: -3, transition: { duration: 0.2, ease: EASE_OUT } },\n whileTap: { scale: 0.985 },\n };\n\n const programs = useMybPrograms({ status: 'ACTIVE', pagination: { limit: 50 } });\n const sessions = useMybSessions({ pagination: { limit: 20 } });\n const goals = useMybGoals({ status: 'ACTIVE', pagination: { limit: 50 } });\n const macros = useMybDailyMacros();\n const weightMetrics = useMybBodyMetrics({ type: 'WEIGHT', pagination: { limit: 30 } });\n const readiness = useMybRecoveryLogs('READINESS');\n\n const programRows = programs.data?.items ?? [];\n const sessionRows = sessions.data?.items ?? [];\n const goalRows = goals.data?.items ?? [];\n const weightRows = weightMetrics.data?.items ?? [];\n const readinessRows = readiness.data ?? [];\n\n const now = new Date();\n const weekAgo = now.getTime() - 7 * 24 * 60 * 60 * 1000;\n const sessionsThisWeek = sessionRows.filter((s) => {\n const ref = s.completedAt ?? s.startedAt ?? s.scheduledFor ?? s.createdAt;\n return ref ? new Date(ref).getTime() >= weekAgo : false;\n });\n const completedThisWeek = sessionsThisWeek.filter((s) => s.status === 'COMPLETED').length;\n const activeSession = sessionRows.find((s) => s.status === 'IN_PROGRESS');\n const upcomingSession = sessionRows\n .filter((s) => s.status === 'SCHEDULED' && s.scheduledFor)\n .sort((a, b) => new Date(a.scheduledFor!).getTime() - new Date(b.scheduledFor!).getTime())[0];\n\n const latestWeight = weightRows[0];\n const priorWeight = weightRows[1];\n const weightDelta =\n latestWeight && priorWeight ? latestWeight.value - priorWeight.value : undefined;\n\n const latestReadiness = readinessRows\n .slice()\n .sort((a, b) => new Date(b.loggedAt).getTime() - new Date(a.loggedAt).getTime())[0];\n\n const goalProgressAvg = useMemo(() => {\n if (goalRows.length === 0) return 0;\n const sum = goalRows.reduce((acc, g) => acc + computeGoalProgress(g), 0);\n return sum / goalRows.length;\n }, [goalRows]);\n\n const macroData = macros.data;\n const caloriePct = macroData?.calorieTarget\n ? (macroData.calories / macroData.calorieTarget) * 100\n : 0;\n\n const isInitialLoading =\n programs.isLoading && sessions.isLoading && goals.isLoading && macros.isLoading;\n\n if (isInitialLoading) {\n return (\n <PageLayout title=\"MyBodyShield\" titleGradient aurora>\n <LoadingView label=\"Loading your fitness overview…\" />\n </PageLayout>\n );\n }\n\n const greetingName = currentUser?.firstName || currentUser?.name || 'there';\n\n const kpiCards: { id: string; node: ReactNode }[] = [\n {\n id: 'programs',\n node: (\n <MetricsCard\n icon={<Dumbbell className=\"h-5 w-5\" />}\n value={formatNumber(programRows.length)}\n label=\"Active programs\"\n footer={\n <p className=\"text-xs text-text-tertiary\">\n <button\n type=\"button\"\n onClick={() => navigateTo('programs')}\n className=\"font-medium text-accent hover:underline\"\n >\n Manage programs\n </button>\n </p>\n }\n />\n ),\n },\n {\n id: 'sessions',\n node: (\n <MetricsCard\n icon={<Flame className=\"h-5 w-5\" />}\n value={formatNumber(completedThisWeek)}\n label=\"Sessions this week\"\n accent={activeSession ? 'warning' : undefined}\n footer={\n activeSession ? (\n <button\n type=\"button\"\n onClick={() => navigateTo(`train/${activeSession.id}`)}\n className=\"text-xs font-medium text-status-warning-text hover:underline\"\n >\n Resume live session →\n </button>\n ) : (\n <p className=\"text-xs text-text-tertiary\">\n <span className=\"tabular-nums text-text-secondary\">{sessionsThisWeek.length}</span>{' '}\n logged\n </p>\n )\n }\n />\n ),\n },\n {\n id: 'goals',\n node: (\n <MetricsCard\n icon={<Target className=\"h-5 w-5\" />}\n value={formatNumber(goalRows.length)}\n label=\"Active goals\"\n footer={\n <div className=\"space-y-2\">\n <MiniBar value={goalProgressAvg} height={4} barClassName=\"bg-action-primary-bg\" />\n <p className=\"text-xs text-text-tertiary\">\n <span className=\"tabular-nums text-text-secondary\">\n {formatPercent(goalProgressAvg)}\n </span>{' '}\n avg progress\n </p>\n </div>\n }\n />\n ),\n },\n {\n id: 'weight',\n node: (\n <MetricsCard\n icon={<Weight className=\"h-5 w-5\" />}\n value={latestWeight ? formatWeight(latestWeight.value) : '—'}\n label=\"Latest weight\"\n footer={\n weightDelta != null ? (\n <p className=\"text-xs text-text-tertiary\">\n <span\n className={\n weightDelta <= 0\n ? 'font-medium text-status-success-text'\n : 'font-medium text-text-secondary'\n }\n >\n {weightDelta > 0 ? '+' : ''}\n {weightDelta.toFixed(1)} kg\n </span>{' '}\n vs last\n </p>\n ) : (\n <button\n type=\"button\"\n onClick={() => navigateTo('progress')}\n className=\"text-xs font-medium text-accent hover:underline\"\n >\n Log a measurement →\n </button>\n )\n }\n />\n ),\n },\n {\n id: 'recovery',\n node: (\n <MetricsCard\n icon={<Moon className=\"h-5 w-5\" />}\n value={latestReadiness ? `${latestReadiness.score}` : '—'}\n label=\"Recovery readiness\"\n accent={\n latestReadiness\n ? latestReadiness.score >= 70\n ? 'success'\n : latestReadiness.score >= 40\n ? 'warning'\n : 'error'\n : undefined\n }\n footer={\n latestReadiness ? (\n <p className=\"text-xs text-text-tertiary\">\n {formatRelativeTime(latestReadiness.loggedAt)}\n </p>\n ) : (\n <button\n type=\"button\"\n onClick={() => navigateTo('recovery')}\n className=\"text-xs font-medium text-accent hover:underline\"\n >\n Log recovery →\n </button>\n )\n }\n />\n ),\n },\n ];\n\n return (\n <PageLayout\n title=\"MyBodyShield\"\n titleGradient\n aurora\n description={`Welcome back, ${greetingName} — your training, nutrition, progress & recovery control room.`}\n >\n {/* KPI grid */}\n <div\n data-tour=\"mybodyshield-dashboard-kpi-grid\"\n className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-5\"\n >\n {kpiCards.map((card, i) => (\n <motion.div\n key={card.id}\n className=\"group relative rounded-2xl transition-shadow duration-300 hover:shadow-float\"\n {...rise(i)}\n {...lift}\n >\n {/* Emerald top-edge accent — brightens on hover */}\n <span\n aria-hidden\n className=\"pointer-events-none absolute inset-x-6 top-0 z-10 h-px rounded-full bg-gradient-to-r from-transparent via-action-primary-bg to-transparent opacity-40 transition-opacity duration-300 group-hover:opacity-90\"\n />\n {card.node}\n </motion.div>\n ))}\n </div>\n\n <div className=\"mt-6 grid grid-cols-1 gap-6 lg:grid-cols-3\">\n {/* Today's nutrition */}\n <motion.div\n data-tour=\"mybodyshield-dashboard-nutrition\"\n className=\"lg:col-span-2\"\n {...rise(0)}\n >\n <GlassCard treatment=\"glass\" className=\"h-full p-6\">\n <div className=\"mb-4 flex flex-wrap items-start justify-between gap-3\">\n <div>\n <h2 className=\"text-base font-semibold text-text-primary\">\n Today&apos;s nutrition\n </h2>\n <p className=\"mt-0.5 text-sm text-text-secondary\">\n Calories & macros logged against your active plan.\n </p>\n </div>\n <button\n type=\"button\"\n onClick={() => navigateTo('nutrition')}\n className=\"inline-flex items-center gap-1 text-sm font-medium text-accent hover:underline\"\n >\n Nutrition <ArrowRight className=\"h-4 w-4\" />\n </button>\n </div>\n {!macroData || (macroData.calorieTarget === 0 && macroData.calories === 0) ? (\n <EmptyState\n illustration=\"empty-data\"\n icon={<Salad className=\"h-10 w-10\" />}\n title=\"No nutrition data yet\"\n description=\"Activate a nutrition plan and log meals to track your daily macros here.\"\n />\n ) : (\n <div className=\"flex flex-col gap-8 sm:flex-row sm:items-center\">\n {/* Calorie ring — the nutrition hero */}\n <div className=\"relative mx-auto flex shrink-0 items-center justify-center sm:mx-0\">\n <span\n aria-hidden\n className=\"pointer-events-none absolute inset-2 rounded-full opacity-70 blur-2xl\"\n style={{\n background:\n 'radial-gradient(circle, color-mix(in srgb, var(--color-action-primary-bg) 26%, transparent), transparent 70%)',\n }}\n />\n <ProgressRing value={caloriePct} size={188} stroke={16}>\n <span className=\"text-[11px] font-medium uppercase tracking-wide text-text-tertiary\">\n Calories\n </span>\n <span className=\"text-3xl font-bold tabular-nums text-text-primary\">\n {formatNumber(macroData.calories)}\n </span>\n <span className=\"mt-0.5 text-xs text-text-tertiary\">\n / {formatNumber(macroData.calorieTarget)} kcal\n </span>\n </ProgressRing>\n </div>\n <div className=\"flex-1 space-y-4\">\n <div className=\"space-y-2\">\n <MacroBar\n proteinG={macroData.proteinG}\n carbsG={macroData.carbsG}\n fatG={macroData.fatG}\n height={12}\n />\n <MacroLegend />\n </div>\n <div className=\"grid grid-cols-3 gap-3\">\n {(\n [\n ['Protein', macroData.proteinG, macroData.proteinTarget],\n ['Carbs', macroData.carbsG, macroData.carbsTarget],\n ['Fat', macroData.fatG, macroData.fatTarget],\n ] as const\n ).map(([label, val, target]) => {\n const pct = target ? (val / target) * 100 : 0;\n return (\n <div\n key={label}\n className=\"rounded-2xl bg-bg-sunken/60 p-3 ring-1 ring-inset ring-border-seam/40 transition-shadow hover:shadow-float\"\n >\n <div className=\"flex items-center justify-between gap-1\">\n <p className=\"text-xs font-medium text-text-tertiary\">{label}</p>\n <span className=\"text-[11px] tabular-nums text-text-tertiary\">\n {formatPercent(pct)}\n </span>\n </div>\n <p className=\"mt-1 text-sm font-semibold tabular-nums text-text-primary\">\n {formatNumber(val)}\n <span className=\"text-xs font-normal text-text-tertiary\">\n {' '}\n / {formatNumber(target)} g\n </span>\n </p>\n <Meter value={pct} height={5} className=\"mt-2\" />\n </div>\n );\n })}\n </div>\n </div>\n </div>\n )}\n </GlassCard>\n </motion.div>\n\n {/* Next up */}\n <motion.div data-tour=\"mybodyshield-dashboard-next-up\" {...rise(1)}>\n <GlassCard treatment=\"glass\" className=\"h-full p-6\">\n <div className=\"mb-4 flex items-center justify-between gap-3\">\n <h2 className=\"text-base font-semibold text-text-primary\">Next up</h2>\n <button\n type=\"button\"\n onClick={() => navigateTo('train')}\n className=\"inline-flex items-center gap-1 text-sm font-medium text-accent hover:underline\"\n >\n Train <ArrowRight className=\"h-4 w-4\" />\n </button>\n </div>\n {activeSession ? (\n <motion.button\n type=\"button\"\n onClick={() => navigateTo(`train/${activeSession.id}`)}\n className=\"w-full rounded-2xl border border-status-warning-text/40 bg-status-warning-bg-subtle p-4 text-left transition-shadow hover:shadow-float\"\n {...liftTap}\n >\n <div className=\"flex items-center gap-2\">\n <StatusBadge status={activeSession.status} />\n </div>\n <p className=\"mt-2 text-sm font-medium text-text-primary\">\n Live workout in progress\n </p>\n <p className=\"mt-0.5 text-xs text-text-tertiary\">\n Started {formatRelativeTime(activeSession.startedAt)}\n </p>\n </motion.button>\n ) : upcomingSession ? (\n <motion.button\n type=\"button\"\n onClick={() => navigateTo(`train/${upcomingSession.id}`)}\n className=\"w-full rounded-2xl bg-bg-sunken/60 p-4 text-left ring-1 ring-inset ring-border-seam/40 transition-shadow hover:shadow-float\"\n {...liftTap}\n >\n <div className=\"flex items-center gap-2\">\n <IconChip icon={<CalendarCheck className=\"h-4 w-4\" />} />\n <div>\n <p className=\"text-sm font-medium text-text-primary\">Scheduled session</p>\n <p className=\"text-xs text-text-tertiary\">\n {formatRelativeTime(upcomingSession.scheduledFor)}\n </p>\n </div>\n </div>\n </motion.button>\n ) : (\n <div className=\"flex flex-col items-center justify-center rounded-2xl border border-dashed border-border-seam py-8 text-center\">\n <span className=\"flex size-11 items-center justify-center rounded-full bg-[var(--color-accent-soft)]\">\n <Flame className=\"h-5 w-5 text-accent\" />\n </span>\n <p className=\"mt-3 text-sm font-medium text-text-primary\">No session scheduled</p>\n <button\n type=\"button\"\n onClick={() => navigateTo('train')}\n className=\"mt-1 text-xs font-medium text-accent hover:underline\"\n >\n Start a workout\n </button>\n </div>\n )}\n </GlassCard>\n </motion.div>\n </div>\n\n {/* Active goals */}\n <Section\n data-tour=\"mybodyshield-dashboard-active-goals\"\n title=\"Active goals\"\n description=\"Your current targets across training, nutrition and body composition.\"\n action={\n <button\n type=\"button\"\n onClick={() => navigateTo('goals')}\n className=\"inline-flex items-center gap-1 text-sm font-medium text-accent hover:underline\"\n >\n All goals <ArrowRight className=\"h-4 w-4\" />\n </button>\n }\n >\n {goalRows.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n icon={<Target className=\"h-10 w-10\" />}\n title=\"No active goals\"\n description=\"Set a goal to give your training and nutrition a clear target to work toward.\"\n />\n ) : (\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n {goalRows.slice(0, 6).map((g, i) => {\n const pct = computeGoalProgress(g);\n const meta = GOAL_TYPE_META[g.type];\n const Icon = meta.icon;\n return (\n <motion.button\n key={g.id}\n type=\"button\"\n onClick={() => navigateTo('goals')}\n className=\"text-left\"\n {...rise(i)}\n {...liftTap}\n >\n <GlassCard\n treatment=\"plain\"\n className=\"h-full p-4 transition-shadow hover:shadow-float\"\n >\n <div className=\"flex items-start gap-3\">\n <IconChip icon={<Icon className=\"h-4 w-4\" />} tint={meta.tint} />\n <div className=\"min-w-0 flex-1\">\n <p className=\"truncate text-sm font-semibold text-text-primary\">\n {g.title}\n </p>\n <p className=\"text-xs text-text-tertiary\">{formatEnumLabel(g.type)}</p>\n </div>\n </div>\n <div className=\"mt-3 space-y-1.5\">\n <Meter value={pct} />\n <div className=\"flex items-center justify-between text-xs\">\n <span className=\"tabular-nums text-text-secondary\">\n {formatNumber(g.currentValue)}\n {g.unit ? ` ${g.unit}` : ''} / {formatNumber(g.targetValue)}\n {g.unit ? ` ${g.unit}` : ''}\n </span>\n <span className=\"tabular-nums text-text-tertiary\">\n {formatPercent(pct)}\n </span>\n </div>\n </div>\n </GlassCard>\n </motion.button>\n );\n })}\n </div>\n )}\n </Section>\n\n {/* Recent sessions */}\n <Section\n data-tour=\"mybodyshield-dashboard-recent-sessions\"\n title=\"Recent sessions\"\n description=\"Your latest logged and scheduled workouts.\"\n action={\n <button\n type=\"button\"\n onClick={() => navigateTo('train')}\n className=\"inline-flex items-center gap-1 text-sm font-medium text-accent hover:underline\"\n >\n All sessions <ArrowRight className=\"h-4 w-4\" />\n </button>\n }\n >\n {sessionRows.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n icon={<Flame className=\"h-10 w-10\" />}\n title=\"No sessions yet\"\n description=\"Start a workout session to begin logging sets and tracking your training.\"\n />\n ) : (\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n {sessionRows.slice(0, 6).map((s, i) => {\n const meta = WORKOUT_TYPE_META.STRENGTH;\n const Icon = s.status === 'COMPLETED' ? CheckCircle2 : meta.icon;\n return (\n <motion.button\n key={s.id}\n type=\"button\"\n onClick={() => navigateTo(`train/${s.id}`)}\n className=\"text-left\"\n {...rise(i)}\n {...liftTap}\n >\n <GlassCard treatment=\"plain\" className=\"p-4 transition-shadow hover:shadow-float\">\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-sm font-medium text-text-primary\">\n {s.caloriesBurned > 0\n ? `${formatNumber(s.caloriesBurned)} kcal burned`\n : 'Workout session'}\n </p>\n <p className=\"mt-0.5 text-xs text-text-tertiary\">\n {formatRelativeTime(s.completedAt ?? s.startedAt ?? s.createdAt)}\n </p>\n </GlassCard>\n </motion.button>\n );\n })}\n </div>\n )}\n </Section>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"DashboardPage.js","names":[],"sources":["../../../src/mybodyshield/pages/DashboardPage.tsx"],"sourcesContent":["/**\n * Dashboard — MyBodyShield health & fitness overview.\n * @module mybodyshield/pages\n *\n * KPIs are aggregated client-side from the real `myb*` list endpoints:\n * active programs, this-week's sessions, goal progress, today's macros vs\n * target, latest body weight and recovery readiness. No mock data.\n */\n\nimport { useMemo } from 'react';\nimport type { ReactNode } from 'react';\nimport { motion, useReducedMotion } from 'framer-motion';\nimport {\n Dumbbell,\n Flame,\n Target,\n Salad,\n Moon,\n ArrowRight,\n CheckCircle2,\n Weight,\n CalendarCheck,\n} from 'lucide-react';\nimport { GlassCard } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../components/PageLayout';\nimport { MetricsCard } from '../components/MetricsCard';\nimport { StatusBadge } from '../components/StatusBadge';\nimport { MiniBar } from '../components/MiniBar';\nimport { LoadingView, Section } from '../components/StateViews';\nimport { EmptyState } from '../components/EmptyState';\nimport {\n Meter,\n ProgressRing,\n MacroBar,\n MacroLegend,\n IconChip,\n GOAL_TYPE_META,\n RECOVERY_META,\n WORKOUT_TYPE_META,\n computeGoalProgress,\n} from './_shared/visuals';\nimport { useMyBodyShield } from '../context';\nimport {\n useMybPrograms,\n useMybSessions,\n useMybGoals,\n useMybDailyMacros,\n useMybBodyMetrics,\n useMybRecoveryLogs,\n} from '../hooks';\n\nimport {\n formatNumber,\n formatRelativeTime,\n formatKcal,\n formatWeight,\n formatPercent,\n formatEnumLabel,\n} from '../utils/formatters';\n\n/** Shared easing for entrance / hover motion (smooth ease-out). */\nconst EASE_OUT: [number, number, number, number] = [0.16, 1, 0.3, 1];\n\nexport function DashboardPage() {\n const { navigateTo, currentUser } = useMyBodyShield();\n const reduceMotion = useReducedMotion();\n\n /** Staggered fade/rise entrance — one-shot, skipped under reduced motion. */\n const rise = (i: number) =>\n reduceMotion\n ? {}\n : {\n initial: { opacity: 0, y: 14 },\n whileInView: { opacity: 1, y: 0 },\n viewport: { once: true, amount: 0.15 },\n transition: { duration: 0.45, delay: Math.min(i, 8) * 0.05, ease: EASE_OUT },\n };\n\n /** Gentle hover lift for cards. */\n const lift = reduceMotion\n ? {}\n : { whileHover: { y: -3, transition: { duration: 0.2, ease: EASE_OUT } } };\n\n /** Hover lift + tap feedback for interactive cards. */\n const liftTap = reduceMotion\n ? {}\n : {\n whileHover: { y: -3, transition: { duration: 0.2, ease: EASE_OUT } },\n whileTap: { scale: 0.985 },\n };\n\n const programs = useMybPrograms({ status: 'ACTIVE', pagination: { limit: 50 } });\n const sessions = useMybSessions({ pagination: { limit: 20 } });\n const goals = useMybGoals({ status: 'ACTIVE', pagination: { limit: 50 } });\n const macros = useMybDailyMacros();\n const weightMetrics = useMybBodyMetrics({ type: 'WEIGHT', pagination: { limit: 30 } });\n const readiness = useMybRecoveryLogs('READINESS');\n\n const programRows = programs.data?.items ?? [];\n const sessionRows = sessions.data?.items ?? [];\n const goalRows = goals.data?.items ?? [];\n const weightRows = weightMetrics.data?.items ?? [];\n const readinessRows = readiness.data?.items ?? [];\n\n const now = new Date();\n const weekAgo = now.getTime() - 7 * 24 * 60 * 60 * 1000;\n const sessionsThisWeek = sessionRows.filter((s) => {\n const ref = s.completedAt ?? s.startedAt ?? s.scheduledFor ?? s.createdAt;\n return ref ? new Date(ref).getTime() >= weekAgo : false;\n });\n const completedThisWeek = sessionsThisWeek.filter((s) => s.status === 'COMPLETED').length;\n const activeSession = sessionRows.find((s) => s.status === 'IN_PROGRESS');\n const upcomingSession = sessionRows\n .filter((s) => s.status === 'SCHEDULED' && s.scheduledFor)\n .sort((a, b) => new Date(a.scheduledFor!).getTime() - new Date(b.scheduledFor!).getTime())[0];\n\n const latestWeight = weightRows[0];\n const priorWeight = weightRows[1];\n const weightDelta =\n latestWeight && priorWeight ? latestWeight.value - priorWeight.value : undefined;\n\n const latestReadiness = readinessRows\n .slice()\n .sort((a, b) => new Date(b.loggedAt).getTime() - new Date(a.loggedAt).getTime())[0];\n\n const goalProgressAvg = useMemo(() => {\n if (goalRows.length === 0) return 0;\n const sum = goalRows.reduce((acc, g) => acc + computeGoalProgress(g), 0);\n return sum / goalRows.length;\n }, [goalRows]);\n\n const macroData = macros.data;\n const caloriePct = macroData?.calorieTarget\n ? (macroData.calories / macroData.calorieTarget) * 100\n : 0;\n\n const isInitialLoading =\n programs.isLoading && sessions.isLoading && goals.isLoading && macros.isLoading;\n\n if (isInitialLoading) {\n return (\n <PageLayout title=\"MyBodyShield\" titleGradient aurora>\n <LoadingView label=\"Loading your fitness overview…\" />\n </PageLayout>\n );\n }\n\n const greetingName = currentUser?.firstName || currentUser?.name || 'there';\n\n const kpiCards: { id: string; node: ReactNode }[] = [\n {\n id: 'programs',\n node: (\n <MetricsCard\n icon={<Dumbbell className=\"h-5 w-5\" />}\n value={formatNumber(programRows.length)}\n label=\"Active programs\"\n footer={\n <p className=\"text-xs text-text-tertiary\">\n <button\n type=\"button\"\n onClick={() => navigateTo('programs')}\n className=\"font-medium text-accent hover:underline\"\n >\n Manage programs\n </button>\n </p>\n }\n />\n ),\n },\n {\n id: 'sessions',\n node: (\n <MetricsCard\n icon={<Flame className=\"h-5 w-5\" />}\n value={formatNumber(completedThisWeek)}\n label=\"Sessions this week\"\n accent={activeSession ? 'warning' : undefined}\n footer={\n activeSession ? (\n <button\n type=\"button\"\n onClick={() => navigateTo(`train/${activeSession.id}`)}\n className=\"text-xs font-medium text-status-warning-text hover:underline\"\n >\n Resume live session →\n </button>\n ) : (\n <p className=\"text-xs text-text-tertiary\">\n <span className=\"tabular-nums text-text-secondary\">{sessionsThisWeek.length}</span>{' '}\n logged\n </p>\n )\n }\n />\n ),\n },\n {\n id: 'goals',\n node: (\n <MetricsCard\n icon={<Target className=\"h-5 w-5\" />}\n value={formatNumber(goalRows.length)}\n label=\"Active goals\"\n footer={\n <div className=\"space-y-2\">\n <MiniBar value={goalProgressAvg} height={4} barClassName=\"bg-action-primary-bg\" />\n <p className=\"text-xs text-text-tertiary\">\n <span className=\"tabular-nums text-text-secondary\">\n {formatPercent(goalProgressAvg)}\n </span>{' '}\n avg progress\n </p>\n </div>\n }\n />\n ),\n },\n {\n id: 'weight',\n node: (\n <MetricsCard\n icon={<Weight className=\"h-5 w-5\" />}\n value={latestWeight ? formatWeight(latestWeight.value) : '—'}\n label=\"Latest weight\"\n footer={\n weightDelta != null ? (\n <p className=\"text-xs text-text-tertiary\">\n <span\n className={\n weightDelta <= 0\n ? 'font-medium text-status-success-text'\n : 'font-medium text-text-secondary'\n }\n >\n {weightDelta > 0 ? '+' : ''}\n {weightDelta.toFixed(1)} kg\n </span>{' '}\n vs last\n </p>\n ) : (\n <button\n type=\"button\"\n onClick={() => navigateTo('progress')}\n className=\"text-xs font-medium text-accent hover:underline\"\n >\n Log a measurement →\n </button>\n )\n }\n />\n ),\n },\n {\n id: 'recovery',\n node: (\n <MetricsCard\n icon={<Moon className=\"h-5 w-5\" />}\n value={latestReadiness ? `${latestReadiness.score}` : '—'}\n label=\"Recovery readiness\"\n accent={\n latestReadiness\n ? latestReadiness.score >= 70\n ? 'success'\n : latestReadiness.score >= 40\n ? 'warning'\n : 'error'\n : undefined\n }\n footer={\n latestReadiness ? (\n <p className=\"text-xs text-text-tertiary\">\n {formatRelativeTime(latestReadiness.loggedAt)}\n </p>\n ) : (\n <button\n type=\"button\"\n onClick={() => navigateTo('recovery')}\n className=\"text-xs font-medium text-accent hover:underline\"\n >\n Log recovery →\n </button>\n )\n }\n />\n ),\n },\n ];\n\n return (\n <PageLayout\n title=\"MyBodyShield\"\n titleGradient\n aurora\n description={`Welcome back, ${greetingName} — your training, nutrition, progress & recovery control room.`}\n >\n {/* KPI grid */}\n <div\n data-tour=\"mybodyshield-dashboard-kpi-grid\"\n className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-5\"\n >\n {kpiCards.map((card, i) => (\n <motion.div\n key={card.id}\n className=\"group relative rounded-2xl transition-shadow duration-300 hover:shadow-float\"\n {...rise(i)}\n {...lift}\n >\n {/* Emerald top-edge accent — brightens on hover */}\n <span\n aria-hidden\n className=\"pointer-events-none absolute inset-x-6 top-0 z-10 h-px rounded-full bg-gradient-to-r from-transparent via-action-primary-bg to-transparent opacity-40 transition-opacity duration-300 group-hover:opacity-90\"\n />\n {card.node}\n </motion.div>\n ))}\n </div>\n\n <div className=\"mt-6 grid grid-cols-1 gap-6 lg:grid-cols-3\">\n {/* Today's nutrition */}\n <motion.div\n data-tour=\"mybodyshield-dashboard-nutrition\"\n className=\"lg:col-span-2\"\n {...rise(0)}\n >\n <GlassCard treatment=\"glass\" className=\"h-full p-6\">\n <div className=\"mb-4 flex flex-wrap items-start justify-between gap-3\">\n <div>\n <h2 className=\"text-base font-semibold text-text-primary\">\n Today&apos;s nutrition\n </h2>\n <p className=\"mt-0.5 text-sm text-text-secondary\">\n Calories & macros logged against your active plan.\n </p>\n </div>\n <button\n type=\"button\"\n onClick={() => navigateTo('nutrition')}\n className=\"inline-flex items-center gap-1 text-sm font-medium text-accent hover:underline\"\n >\n Nutrition <ArrowRight className=\"h-4 w-4\" />\n </button>\n </div>\n {!macroData || (macroData.calorieTarget === 0 && macroData.calories === 0) ? (\n <EmptyState\n illustration=\"empty-data\"\n icon={<Salad className=\"h-10 w-10\" />}\n title=\"No nutrition data yet\"\n description=\"Activate a nutrition plan and log meals to track your daily macros here.\"\n />\n ) : (\n <div className=\"flex flex-col gap-8 sm:flex-row sm:items-center\">\n {/* Calorie ring — the nutrition hero */}\n <div className=\"relative mx-auto flex shrink-0 items-center justify-center sm:mx-0\">\n <span\n aria-hidden\n className=\"pointer-events-none absolute inset-2 rounded-full opacity-70 blur-2xl\"\n style={{\n background:\n 'radial-gradient(circle, color-mix(in srgb, var(--color-action-primary-bg) 26%, transparent), transparent 70%)',\n }}\n />\n <ProgressRing value={caloriePct} size={188} stroke={16}>\n <span className=\"text-[11px] font-medium uppercase tracking-wide text-text-tertiary\">\n Calories\n </span>\n <span className=\"text-3xl font-bold tabular-nums text-text-primary\">\n {formatNumber(macroData.calories)}\n </span>\n <span className=\"mt-0.5 text-xs text-text-tertiary\">\n / {formatNumber(macroData.calorieTarget)} kcal\n </span>\n </ProgressRing>\n </div>\n <div className=\"flex-1 space-y-4\">\n <div className=\"space-y-2\">\n <MacroBar\n proteinG={macroData.proteinG}\n carbsG={macroData.carbsG}\n fatG={macroData.fatG}\n height={12}\n />\n <MacroLegend />\n </div>\n <div className=\"grid grid-cols-3 gap-3\">\n {(\n [\n ['Protein', macroData.proteinG, macroData.proteinTarget],\n ['Carbs', macroData.carbsG, macroData.carbsTarget],\n ['Fat', macroData.fatG, macroData.fatTarget],\n ] as const\n ).map(([label, val, target]) => {\n const pct = target ? (val / target) * 100 : 0;\n return (\n <div\n key={label}\n className=\"rounded-2xl bg-bg-sunken/60 p-3 ring-1 ring-inset ring-border-seam/40 transition-shadow hover:shadow-float\"\n >\n <div className=\"flex items-center justify-between gap-1\">\n <p className=\"text-xs font-medium text-text-tertiary\">{label}</p>\n <span className=\"text-[11px] tabular-nums text-text-tertiary\">\n {formatPercent(pct)}\n </span>\n </div>\n <p className=\"mt-1 text-sm font-semibold tabular-nums text-text-primary\">\n {formatNumber(val)}\n <span className=\"text-xs font-normal text-text-tertiary\">\n {' '}\n / {formatNumber(target)} g\n </span>\n </p>\n <Meter value={pct} height={5} className=\"mt-2\" />\n </div>\n );\n })}\n </div>\n </div>\n </div>\n )}\n </GlassCard>\n </motion.div>\n\n {/* Next up */}\n <motion.div data-tour=\"mybodyshield-dashboard-next-up\" {...rise(1)}>\n <GlassCard treatment=\"glass\" className=\"h-full p-6\">\n <div className=\"mb-4 flex items-center justify-between gap-3\">\n <h2 className=\"text-base font-semibold text-text-primary\">Next up</h2>\n <button\n type=\"button\"\n onClick={() => navigateTo('train')}\n className=\"inline-flex items-center gap-1 text-sm font-medium text-accent hover:underline\"\n >\n Train <ArrowRight className=\"h-4 w-4\" />\n </button>\n </div>\n {activeSession ? (\n <motion.button\n type=\"button\"\n onClick={() => navigateTo(`train/${activeSession.id}`)}\n className=\"w-full rounded-2xl border border-status-warning-text/40 bg-status-warning-bg-subtle p-4 text-left transition-shadow hover:shadow-float\"\n {...liftTap}\n >\n <div className=\"flex items-center gap-2\">\n <StatusBadge status={activeSession.status} />\n </div>\n <p className=\"mt-2 text-sm font-medium text-text-primary\">\n Live workout in progress\n </p>\n <p className=\"mt-0.5 text-xs text-text-tertiary\">\n Started {formatRelativeTime(activeSession.startedAt)}\n </p>\n </motion.button>\n ) : upcomingSession ? (\n <motion.button\n type=\"button\"\n onClick={() => navigateTo(`train/${upcomingSession.id}`)}\n className=\"w-full rounded-2xl bg-bg-sunken/60 p-4 text-left ring-1 ring-inset ring-border-seam/40 transition-shadow hover:shadow-float\"\n {...liftTap}\n >\n <div className=\"flex items-center gap-2\">\n <IconChip icon={<CalendarCheck className=\"h-4 w-4\" />} />\n <div>\n <p className=\"text-sm font-medium text-text-primary\">Scheduled session</p>\n <p className=\"text-xs text-text-tertiary\">\n {formatRelativeTime(upcomingSession.scheduledFor)}\n </p>\n </div>\n </div>\n </motion.button>\n ) : (\n <div className=\"flex flex-col items-center justify-center rounded-2xl border border-dashed border-border-seam py-8 text-center\">\n <span className=\"flex size-11 items-center justify-center rounded-full bg-[var(--color-accent-soft)]\">\n <Flame className=\"h-5 w-5 text-accent\" />\n </span>\n <p className=\"mt-3 text-sm font-medium text-text-primary\">No session scheduled</p>\n <button\n type=\"button\"\n onClick={() => navigateTo('train')}\n className=\"mt-1 text-xs font-medium text-accent hover:underline\"\n >\n Start a workout\n </button>\n </div>\n )}\n </GlassCard>\n </motion.div>\n </div>\n\n {/* Active goals */}\n <Section\n data-tour=\"mybodyshield-dashboard-active-goals\"\n title=\"Active goals\"\n description=\"Your current targets across training, nutrition and body composition.\"\n action={\n <button\n type=\"button\"\n onClick={() => navigateTo('goals')}\n className=\"inline-flex items-center gap-1 text-sm font-medium text-accent hover:underline\"\n >\n All goals <ArrowRight className=\"h-4 w-4\" />\n </button>\n }\n >\n {goalRows.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n icon={<Target className=\"h-10 w-10\" />}\n title=\"No active goals\"\n description=\"Set a goal to give your training and nutrition a clear target to work toward.\"\n />\n ) : (\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n {goalRows.slice(0, 6).map((g, i) => {\n const pct = computeGoalProgress(g);\n const meta = GOAL_TYPE_META[g.type];\n const Icon = meta.icon;\n return (\n <motion.button\n key={g.id}\n type=\"button\"\n onClick={() => navigateTo('goals')}\n className=\"text-left\"\n {...rise(i)}\n {...liftTap}\n >\n <GlassCard\n treatment=\"plain\"\n className=\"h-full p-4 transition-shadow hover:shadow-float\"\n >\n <div className=\"flex items-start gap-3\">\n <IconChip icon={<Icon className=\"h-4 w-4\" />} tint={meta.tint} />\n <div className=\"min-w-0 flex-1\">\n <p className=\"truncate text-sm font-semibold text-text-primary\">\n {g.title}\n </p>\n <p className=\"text-xs text-text-tertiary\">{formatEnumLabel(g.type)}</p>\n </div>\n </div>\n <div className=\"mt-3 space-y-1.5\">\n <Meter value={pct} />\n <div className=\"flex items-center justify-between text-xs\">\n <span className=\"tabular-nums text-text-secondary\">\n {formatNumber(g.currentValue)}\n {g.unit ? ` ${g.unit}` : ''} / {formatNumber(g.targetValue)}\n {g.unit ? ` ${g.unit}` : ''}\n </span>\n <span className=\"tabular-nums text-text-tertiary\">\n {formatPercent(pct)}\n </span>\n </div>\n </div>\n </GlassCard>\n </motion.button>\n );\n })}\n </div>\n )}\n </Section>\n\n {/* Recent sessions */}\n <Section\n data-tour=\"mybodyshield-dashboard-recent-sessions\"\n title=\"Recent sessions\"\n description=\"Your latest logged and scheduled workouts.\"\n action={\n <button\n type=\"button\"\n onClick={() => navigateTo('train')}\n className=\"inline-flex items-center gap-1 text-sm font-medium text-accent hover:underline\"\n >\n All sessions <ArrowRight className=\"h-4 w-4\" />\n </button>\n }\n >\n {sessionRows.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n icon={<Flame className=\"h-10 w-10\" />}\n title=\"No sessions yet\"\n description=\"Start a workout session to begin logging sets and tracking your training.\"\n />\n ) : (\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n {sessionRows.slice(0, 6).map((s, i) => {\n const meta = WORKOUT_TYPE_META.STRENGTH;\n const Icon = s.status === 'COMPLETED' ? CheckCircle2 : meta.icon;\n return (\n <motion.button\n key={s.id}\n type=\"button\"\n onClick={() => navigateTo(`train/${s.id}`)}\n className=\"text-left\"\n {...rise(i)}\n {...liftTap}\n >\n <GlassCard treatment=\"plain\" className=\"p-4 transition-shadow hover:shadow-float\">\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-sm font-medium text-text-primary\">\n {s.caloriesBurned > 0\n ? `${formatNumber(s.caloriesBurned)} kcal burned`\n : 'Workout session'}\n </p>\n <p className=\"mt-0.5 text-xs text-text-tertiary\">\n {formatRelativeTime(s.completedAt ?? s.startedAt ?? s.createdAt)}\n </p>\n </GlassCard>\n </motion.button>\n );\n })}\n </div>\n )}\n </Section>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
@@ -420,7 +420,7 @@ function te({ relationship: e, currentUserId: t, busy: i, onTransition: a }) {
420
420
  });
421
421
  }
422
422
  function ne() {
423
- let { currentUser: n } = e(), r = h(), [l, d] = v("ALL"), [f, p] = v(!1), [g, y] = v(null), [b, x] = v(null), S = c(), C = u(), w = S.data ?? [], E = _(() => {
423
+ let { currentUser: n } = e(), r = h(), [l, d] = v("ALL"), [f, p] = v(!1), [g, y] = v(null), [b, x] = v(null), S = c(), C = u(), w = S.data?.items ?? [], E = _(() => {
424
424
  let e = {
425
425
  PENDING: 0,
426
426
  ACTIVE: 0,
@@ -1 +1 @@
1
- {"version":3,"file":"CoachingPage.js","names":[],"sources":["../../../../src/mybodyshield/pages/coaching/CoachingPage.tsx"],"sourcesContent":["/**\n * Coaching — coach ↔ member relationships and shared data scope.\n * @module mybodyshield/pages/coaching\n *\n * Lists the member's `MybCoachingRelationship`s (a PLAIN ARRAY from\n * `mybListCoaching` — no pagination) with a status filter, per-relationship\n * sharing scope (workouts / nutrition / metrics), lifecycle transition actions\n * (accept / pause / resume / end) and a create flow. No mock data.\n */\n\nimport { useMemo, useState } from 'react';\nimport { motion, AnimatePresence, useReducedMotion } from 'framer-motion';\nimport {\n HeartHandshake,\n Plus,\n Dumbbell,\n Salad,\n LineChart,\n CalendarClock,\n CalendarX,\n Check,\n Minus,\n MessageSquare,\n ChevronDown,\n ChevronUp,\n User,\n} from 'lucide-react';\nimport {\n Button,\n Input,\n Label,\n Switch,\n GlassCard,\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n ResponsiveDialogDescription,\n ResponsiveDialogFooter,\n} from '@burdenoff/fe-libs/ui';\nimport {\n PageLayout,\n StatusBadge,\n EmptyState,\n LoadingView,\n ErrorView,\n FilterTabs,\n MybDiscussion,\n UserSearchSelect,\n type FilterOption,\n type UserSelectValue,\n} from '../../components';\nimport { IconChip } from '../_shared/visuals';\nimport { useMyBodyShield } from '../../context';\nimport { useMybCoaching, useMybCreateCoaching, useMybTransitionCoaching } from '../../hooks';\nimport type {\n MybCoachingRelationship,\n MybCoachingStatus,\n MybCreateCoachingInput,\n} from '../../types';\nimport { formatDate } from '../../utils/formatters';\n\ntype StatusFilter = MybCoachingStatus | 'ALL';\n\nconst FILTER_ORDER: MybCoachingStatus[] = ['PENDING', 'ACTIVE', 'PAUSED', 'ENDED'];\n\n/** Shared entrance easing — a soft, decelerating cubic-bezier. */\nconst MOTION_EASE: [number, number, number, number] = [0.16, 1, 0.3, 1];\n\nconst STAGGER_CONTAINER = {\n hidden: {},\n show: { transition: { staggerChildren: 0.06 } },\n};\nconst RISE_ITEM = {\n hidden: { opacity: 0, y: 12 },\n show: { opacity: 1, y: 0, transition: { duration: 0.35, ease: MOTION_EASE } },\n};\n\n/** Allowed lifecycle transitions per current status (contract-defined). */\nconst TRANSITIONS: Record<\n MybCoachingStatus,\n { to: MybCoachingStatus; label: string; primary: boolean }[]\n> = {\n PENDING: [{ to: 'ACTIVE', label: 'Accept', primary: true }],\n ACTIVE: [\n { to: 'PAUSED', label: 'Pause', primary: false },\n { to: 'ENDED', label: 'End', primary: false },\n ],\n PAUSED: [\n { to: 'ACTIVE', label: 'Resume', primary: true },\n { to: 'ENDED', label: 'End', primary: false },\n ],\n ENDED: [],\n};\n\n/** Shorten an opaque actor id for compact display (full id kept as a title). */\nfunction shortId(id: string): string {\n return id.length <= 10 ? id : `${id.slice(0, 8)}…`;\n}\n\n// ============================================================================\n// Create-coaching dialog\n// ============================================================================\n\ninterface CreateCoachingForm {\n memberId: string;\n memberName: string;\n coachId: string;\n coachName: string;\n specialty: string;\n shareWorkouts: boolean;\n shareNutrition: boolean;\n shareMetrics: boolean;\n}\n\nfunction buildEmptyForm(defaultMember: UserSelectValue): CreateCoachingForm {\n return {\n memberId: defaultMember.userId,\n memberName: defaultMember.displayName,\n coachId: '',\n coachName: '',\n specialty: '',\n shareWorkouts: true,\n shareNutrition: true,\n shareMetrics: true,\n };\n}\n\nfunction ShareToggle({\n id,\n label,\n description,\n checked,\n onChange,\n}: {\n id: string;\n label: string;\n description: string;\n checked: boolean;\n onChange: (value: boolean) => void;\n}) {\n return (\n <div className=\"flex items-center justify-between gap-4 rounded-xl bg-bg-sunken/60 p-3\">\n <div className=\"min-w-0\">\n <Label htmlFor={id} className=\"cursor-pointer\">\n {label}\n </Label>\n <p className=\"text-xs text-text-tertiary\">{description}</p>\n </div>\n <Switch id={id} checked={checked} onCheckedChange={onChange} />\n </div>\n );\n}\n\nfunction CreateCoachingDialog({\n open,\n onOpenChange,\n defaultMember,\n}: {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n defaultMember: UserSelectValue;\n}) {\n const create = useMybCreateCoaching();\n const [form, setForm] = useState<CreateCoachingForm>(() => buildEmptyForm(defaultMember));\n const [error, setError] = useState<string | null>(null);\n\n const handleOpenChange = (next: boolean) => {\n if (!next) {\n setForm(buildEmptyForm(defaultMember));\n setError(null);\n }\n onOpenChange(next);\n };\n\n const submit = async () => {\n const coachId = form.coachId.trim();\n const memberId = form.memberId.trim();\n if (!coachId) {\n setError('Coach user id is required.');\n return;\n }\n if (!memberId) {\n setError('Member user id is required.');\n return;\n }\n setError(null);\n const input: MybCreateCoachingInput = {\n coachId,\n memberId,\n specialty: form.specialty.trim() || undefined,\n shareWorkouts: form.shareWorkouts,\n shareNutrition: form.shareNutrition,\n shareMetrics: form.shareMetrics,\n };\n try {\n await create.mutateAsync(input);\n setForm(buildEmptyForm(defaultMember));\n onOpenChange(false);\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to create coaching relationship.');\n }\n };\n\n return (\n <ResponsiveDialog open={open} onOpenChange={handleOpenChange}>\n <ResponsiveDialogContent>\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>New coaching</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n Link a coach and member, and choose exactly what training, nutrition and progress data\n is shared.\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n <form\n className=\"space-y-4\"\n onSubmit={(event) => {\n event.preventDefault();\n void submit();\n }}\n >\n {error && (\n <p className=\"rounded-lg bg-status-error-bg-subtle px-3 py-2 text-sm text-status-error-text\">\n {error}\n </p>\n )}\n <UserSearchSelect\n label=\"Coach\"\n required\n htmlId=\"myb-coaching-coach\"\n userId={form.coachId}\n displayName={form.coachName}\n onSelect={({ userId, displayName }) =>\n setForm((f) => ({ ...f, coachId: userId, coachName: displayName }))\n }\n />\n <UserSearchSelect\n label=\"Member\"\n htmlId=\"myb-coaching-member\"\n userId={form.memberId}\n displayName={form.memberName}\n onSelect={({ userId, displayName }) =>\n setForm((f) => ({ ...f, memberId: userId, memberName: displayName }))\n }\n />\n <div className=\"space-y-2\">\n <Label htmlFor=\"myb-coaching-specialty\">Specialty</Label>\n <Input\n id=\"myb-coaching-specialty\"\n value={form.specialty}\n onChange={(event) => setForm((f) => ({ ...f, specialty: event.target.value }))}\n placeholder=\"e.g., Strength & conditioning\"\n />\n </div>\n <div className=\"space-y-2\">\n <p className=\"text-sm font-medium text-text-primary\">Shared with the coach</p>\n <ShareToggle\n id=\"myb-coaching-share-workouts\"\n label=\"Workouts\"\n description=\"Programs, sessions and logged sets.\"\n checked={form.shareWorkouts}\n onChange={(value) => setForm((f) => ({ ...f, shareWorkouts: value }))}\n />\n <ShareToggle\n id=\"myb-coaching-share-nutrition\"\n label=\"Nutrition\"\n description=\"Plans, meals and daily macros.\"\n checked={form.shareNutrition}\n onChange={(value) => setForm((f) => ({ ...f, shareNutrition: value }))}\n />\n <ShareToggle\n id=\"myb-coaching-share-metrics\"\n label=\"Metrics\"\n description=\"Body metrics and recovery logs.\"\n checked={form.shareMetrics}\n onChange={(value) => setForm((f) => ({ ...f, shareMetrics: value }))}\n />\n </div>\n <ResponsiveDialogFooter>\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={() => handleOpenChange(false)}\n disabled={create.isPending}\n >\n Cancel\n </Button>\n <Button type=\"submit\" disabled={create.isPending}>\n {create.isPending ? 'Creating…' : 'Create coaching'}\n </Button>\n </ResponsiveDialogFooter>\n </form>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n );\n}\n\n// ============================================================================\n// Sharing scope chips\n// ============================================================================\n\nfunction ShareChip({ label, icon, on }: { label: string; icon: typeof Dumbbell; on: boolean }) {\n const Icon = icon;\n const StatusIcon = on ? Check : Minus;\n return (\n <span\n aria-label={`${label} ${on ? 'shared' : 'not shared'}`}\n className={`inline-flex items-center gap-1.5 rounded-full py-0.5 pl-1 pr-2.5 text-xs font-medium ${\n on\n ? 'bg-status-success-bg-subtle text-status-success-text ring-1 ring-inset ring-[color-mix(in_oklab,var(--color-status-success-text)_28%,transparent)]'\n : 'bg-bg-sunken text-text-tertiary ring-1 ring-inset ring-border-seam'\n }`}\n >\n <span\n aria-hidden=\"true\"\n className={`inline-flex size-4 items-center justify-center rounded-full ${\n on\n ? 'bg-[color-mix(in_oklab,var(--color-status-success-text)_22%,transparent)]'\n : 'bg-[color-mix(in_oklab,var(--color-text-tertiary)_18%,transparent)]'\n }`}\n >\n <StatusIcon className=\"h-2.5 w-2.5\" />\n </span>\n <Icon className=\"h-3 w-3\" aria-hidden=\"true\" />\n <span>\n {label} · {on ? 'On' : 'Off'}\n </span>\n </span>\n );\n}\n\n// ============================================================================\n// Coaching card\n// ============================================================================\n\nfunction CoachingCard({\n relationship,\n currentUserId,\n busy,\n onTransition,\n}: {\n relationship: MybCoachingRelationship;\n currentUserId?: string;\n busy: boolean;\n onTransition: (id: string, status: MybCoachingStatus) => void;\n}) {\n const coachIsYou = Boolean(currentUserId) && relationship.coachId === currentUserId;\n const memberIsYou = Boolean(currentUserId) && relationship.memberId === currentUserId;\n const actions = TRANSITIONS[relationship.status];\n const canDiscuss = relationship.status === 'ACTIVE' || relationship.status === 'PAUSED';\n const [showDiscussion, setShowDiscussion] = useState(false);\n const reduce = useReducedMotion();\n\n const isActive = relationship.status === 'ACTIVE';\n const isPending = relationship.status === 'PENDING';\n\n return (\n <GlassCard\n treatment=\"glass\"\n glow={isActive}\n className={\n 'flex h-full flex-col gap-4 p-5 transition-shadow hover:shadow-float' +\n (isPending\n ? ' ring-1 ring-inset ring-[color-mix(in_oklab,var(--color-status-warning-text)_35%,transparent)]'\n : '')\n }\n >\n <div className=\"flex items-start justify-between gap-3\">\n <div className=\"flex min-w-0 items-start gap-3\">\n <IconChip icon={<HeartHandshake className=\"h-4 w-4\" />} />\n <div className=\"min-w-0\">\n <p className=\"truncate text-sm font-semibold text-text-primary\">\n {relationship.specialty?.trim() || 'Coaching relationship'}\n </p>\n <p className=\"text-xs text-text-tertiary\">Coach ↔ member</p>\n </div>\n </div>\n <StatusBadge status={relationship.status} />\n </div>\n\n <div className=\"grid grid-cols-2 gap-3\">\n <div className=\"flex items-center gap-2.5 rounded-lg bg-bg-sunken/60 p-2.5\">\n <span\n aria-hidden=\"true\"\n className={`flex size-8 shrink-0 items-center justify-center rounded-full ring-1 ring-inset ${\n coachIsYou\n ? 'bg-[color-mix(in_oklab,var(--color-action-primary-bg)_16%,transparent)] text-action-primary-bg ring-[color-mix(in_oklab,var(--color-action-primary-bg)_40%,transparent)]'\n : 'bg-bg-surface text-text-tertiary ring-border-seam'\n }`}\n >\n <User className=\"h-4 w-4\" />\n </span>\n <div className=\"min-w-0\">\n <p className=\"text-[11px] font-medium uppercase tracking-wide text-text-tertiary\">\n Coach\n </p>\n <p\n className=\"mt-0.5 truncate text-sm font-medium text-text-primary\"\n title={relationship.coachId}\n >\n {coachIsYou ? 'You' : `#${shortId(relationship.coachId)}`}\n </p>\n </div>\n </div>\n <div className=\"flex items-center gap-2.5 rounded-lg bg-bg-sunken/60 p-2.5\">\n <span\n aria-hidden=\"true\"\n className={`flex size-8 shrink-0 items-center justify-center rounded-full ring-1 ring-inset ${\n memberIsYou\n ? 'bg-[color-mix(in_oklab,var(--color-action-primary-bg)_16%,transparent)] text-action-primary-bg ring-[color-mix(in_oklab,var(--color-action-primary-bg)_40%,transparent)]'\n : 'bg-bg-surface text-text-tertiary ring-border-seam'\n }`}\n >\n <User className=\"h-4 w-4\" />\n </span>\n <div className=\"min-w-0\">\n <p className=\"text-[11px] font-medium uppercase tracking-wide text-text-tertiary\">\n Member\n </p>\n <p\n className=\"mt-0.5 truncate text-sm font-medium text-text-primary\"\n title={relationship.memberId}\n >\n {memberIsYou ? 'You' : `#${shortId(relationship.memberId)}`}\n </p>\n </div>\n </div>\n </div>\n\n <div className=\"flex flex-wrap gap-1.5\">\n <ShareChip label=\"Workouts\" icon={Dumbbell} on={relationship.shareWorkouts} />\n <ShareChip label=\"Nutrition\" icon={Salad} on={relationship.shareNutrition} />\n <ShareChip label=\"Metrics\" icon={LineChart} on={relationship.shareMetrics} />\n </div>\n\n {(relationship.startedAt || relationship.endedAt) && (\n <div className=\"flex flex-wrap items-center gap-x-4 gap-y-1 text-xs text-text-tertiary\">\n {relationship.startedAt && (\n <span className=\"inline-flex items-center gap-1\">\n <CalendarClock className=\"h-3.5 w-3.5\" />\n Started {formatDate(relationship.startedAt)}\n </span>\n )}\n {relationship.endedAt && (\n <span className=\"inline-flex items-center gap-1\">\n <CalendarX className=\"h-3.5 w-3.5\" />\n Ended {formatDate(relationship.endedAt)}\n </span>\n )}\n </div>\n )}\n\n {actions.length > 0 && (\n <div className=\"mt-auto flex flex-wrap gap-2 pt-1\">\n {actions.map((action) => (\n <Button\n key={action.to}\n size=\"sm\"\n variant={action.primary ? undefined : 'outline'}\n disabled={busy}\n onClick={() => onTransition(relationship.id, action.to)}\n >\n {action.label}\n </Button>\n ))}\n </div>\n )}\n\n {canDiscuss && (\n <div\n className={`border-t border-border-seam pt-3 ${actions.length === 0 ? 'mt-auto' : ''}`}\n >\n <button\n type=\"button\"\n onClick={() => setShowDiscussion((prev) => !prev)}\n aria-expanded={showDiscussion}\n className=\"inline-flex items-center gap-1.5 text-sm font-medium text-accent hover:underline\"\n >\n <MessageSquare className=\"h-4 w-4\" />\n {showDiscussion ? 'Hide discussion' : 'Discussion'}\n {showDiscussion ? (\n <ChevronUp className=\"h-4 w-4\" />\n ) : (\n <ChevronDown className=\"h-4 w-4\" />\n )}\n </button>\n <AnimatePresence initial={false}>\n {showDiscussion && (\n <motion.div\n key=\"discussion\"\n initial={reduce ? false : { opacity: 0, height: 0 }}\n animate={{ opacity: 1, height: 'auto' }}\n exit={reduce ? { opacity: 0 } : { opacity: 0, height: 0 }}\n transition={reduce ? { duration: 0 } : { duration: 0.25, ease: MOTION_EASE }}\n className=\"overflow-hidden\"\n >\n <div className=\"mt-3\">\n <MybDiscussion\n entityType=\"mybodyshield-coaching\"\n entityId={relationship.id}\n title=\"Coaching discussion\"\n description=\"A private thread between the coach and member.\"\n />\n </div>\n </motion.div>\n )}\n </AnimatePresence>\n </div>\n )}\n </GlassCard>\n );\n}\n\n// ============================================================================\n// Page\n// ============================================================================\n\nexport function CoachingPage() {\n const { currentUser } = useMyBodyShield();\n const reduce = useReducedMotion();\n const [statusFilter, setStatusFilter] = useState<StatusFilter>('ALL');\n const [createOpen, setCreateOpen] = useState(false);\n const [transitioningId, setTransitioningId] = useState<string | null>(null);\n const [actionError, setActionError] = useState<string | null>(null);\n\n // `useMybCoaching` returns a PLAIN ARRAY — fetch every relationship once and\n // filter client-side so the tab counts stay accurate across filters.\n const coaching = useMybCoaching();\n const transition = useMybTransitionCoaching();\n\n const all = coaching.data ?? [];\n\n const counts = useMemo(() => {\n const map: Record<MybCoachingStatus, number> = {\n PENDING: 0,\n ACTIVE: 0,\n PAUSED: 0,\n ENDED: 0,\n };\n for (const rel of all) map[rel.status] += 1;\n return map;\n }, [all]);\n\n const statusFilters = useMemo<FilterOption<StatusFilter>[]>(\n () => [\n { value: 'ALL', label: 'All', count: all.length },\n ...FILTER_ORDER.map((status) => ({\n value: status as StatusFilter,\n label: status.charAt(0) + status.slice(1).toLowerCase(),\n count: counts[status],\n })),\n ],\n [all.length, counts]\n );\n\n const rows = useMemo(\n () => (statusFilter === 'ALL' ? all : all.filter((rel) => rel.status === statusFilter)),\n [all, statusFilter]\n );\n\n const handleTransition = async (id: string, status: MybCoachingStatus) => {\n setTransitioningId(id);\n setActionError(null);\n try {\n await transition.mutateAsync({ id, status });\n } catch (err) {\n setActionError(err instanceof Error ? err.message : 'Failed to update the relationship.');\n } finally {\n setTransitioningId(null);\n }\n };\n\n const showLoading = coaching.isLoading && !coaching.data;\n\n return (\n <PageLayout\n title=\"Coaching\"\n description=\"Coach ↔ member relationships and what's shared.\"\n actions={\n <Button onClick={() => setCreateOpen(true)}>\n <Plus className=\"mr-2 h-4 w-4\" />\n New coaching\n </Button>\n }\n >\n <div className=\"mb-6\">\n <FilterTabs options={statusFilters} value={statusFilter} onChange={setStatusFilter} />\n </div>\n\n {actionError && (\n <p className=\"mb-4 rounded-lg bg-status-error-bg-subtle px-3 py-2 text-sm text-status-error-text\">\n {actionError}\n </p>\n )}\n\n {showLoading ? (\n <LoadingView label=\"Loading coaching relationships…\" />\n ) : coaching.isError ? (\n <ErrorView\n message=\"We couldn't load your coaching relationships.\"\n onRetry={() => void coaching.refetch()}\n />\n ) : rows.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n icon={<HeartHandshake className=\"h-10 w-10\" />}\n title={\n all.length === 0 ? 'No coaching relationships' : 'No relationships match this filter'\n }\n description={\n all.length === 0\n ? 'Connect a coach and a member to start sharing training, nutrition and progress data.'\n : 'Try a different status filter, or create a new coaching relationship.'\n }\n action={\n <Button onClick={() => setCreateOpen(true)}>\n <Plus className=\"mr-2 h-4 w-4\" />\n New coaching\n </Button>\n }\n />\n ) : (\n <motion.div\n className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3\"\n variants={reduce ? undefined : STAGGER_CONTAINER}\n initial={reduce ? false : 'hidden'}\n animate=\"show\"\n >\n {rows.map((relationship) => (\n <motion.div\n key={relationship.id}\n className=\"h-full\"\n variants={reduce ? undefined : RISE_ITEM}\n >\n <CoachingCard\n relationship={relationship}\n currentUserId={currentUser?.id}\n busy={transitioningId === relationship.id}\n onTransition={(id, status) => void handleTransition(id, status)}\n />\n </motion.div>\n ))}\n </motion.div>\n )}\n\n <CreateCoachingDialog\n open={createOpen}\n onOpenChange={setCreateOpen}\n defaultMember={{\n userId: currentUser?.id ?? '',\n displayName: currentUser?.name || currentUser?.email || '',\n }}\n />\n </PageLayout>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"CoachingPage.js","names":[],"sources":["../../../../src/mybodyshield/pages/coaching/CoachingPage.tsx"],"sourcesContent":["/**\n * Coaching — coach ↔ member relationships and shared data scope.\n * @module mybodyshield/pages/coaching\n *\n * Lists the member's `MybCoachingRelationship`s (a PLAIN ARRAY from\n * `mybListCoaching` — no pagination) with a status filter, per-relationship\n * sharing scope (workouts / nutrition / metrics), lifecycle transition actions\n * (accept / pause / resume / end) and a create flow. No mock data.\n */\n\nimport { useMemo, useState } from 'react';\nimport { motion, AnimatePresence, useReducedMotion } from 'framer-motion';\nimport {\n HeartHandshake,\n Plus,\n Dumbbell,\n Salad,\n LineChart,\n CalendarClock,\n CalendarX,\n Check,\n Minus,\n MessageSquare,\n ChevronDown,\n ChevronUp,\n User,\n} from 'lucide-react';\nimport {\n Button,\n Input,\n Label,\n Switch,\n GlassCard,\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n ResponsiveDialogDescription,\n ResponsiveDialogFooter,\n} from '@burdenoff/fe-libs/ui';\nimport {\n PageLayout,\n StatusBadge,\n EmptyState,\n LoadingView,\n ErrorView,\n FilterTabs,\n MybDiscussion,\n UserSearchSelect,\n type FilterOption,\n type UserSelectValue,\n} from '../../components';\nimport { IconChip } from '../_shared/visuals';\nimport { useMyBodyShield } from '../../context';\nimport { useMybCoaching, useMybCreateCoaching, useMybTransitionCoaching } from '../../hooks';\nimport type {\n MybCoachingRelationship,\n MybCoachingStatus,\n MybCreateCoachingInput,\n} from '../../types';\nimport { formatDate } from '../../utils/formatters';\n\ntype StatusFilter = MybCoachingStatus | 'ALL';\n\nconst FILTER_ORDER: MybCoachingStatus[] = ['PENDING', 'ACTIVE', 'PAUSED', 'ENDED'];\n\n/** Shared entrance easing — a soft, decelerating cubic-bezier. */\nconst MOTION_EASE: [number, number, number, number] = [0.16, 1, 0.3, 1];\n\nconst STAGGER_CONTAINER = {\n hidden: {},\n show: { transition: { staggerChildren: 0.06 } },\n};\nconst RISE_ITEM = {\n hidden: { opacity: 0, y: 12 },\n show: { opacity: 1, y: 0, transition: { duration: 0.35, ease: MOTION_EASE } },\n};\n\n/** Allowed lifecycle transitions per current status (contract-defined). */\nconst TRANSITIONS: Record<\n MybCoachingStatus,\n { to: MybCoachingStatus; label: string; primary: boolean }[]\n> = {\n PENDING: [{ to: 'ACTIVE', label: 'Accept', primary: true }],\n ACTIVE: [\n { to: 'PAUSED', label: 'Pause', primary: false },\n { to: 'ENDED', label: 'End', primary: false },\n ],\n PAUSED: [\n { to: 'ACTIVE', label: 'Resume', primary: true },\n { to: 'ENDED', label: 'End', primary: false },\n ],\n ENDED: [],\n};\n\n/** Shorten an opaque actor id for compact display (full id kept as a title). */\nfunction shortId(id: string): string {\n return id.length <= 10 ? id : `${id.slice(0, 8)}…`;\n}\n\n// ============================================================================\n// Create-coaching dialog\n// ============================================================================\n\ninterface CreateCoachingForm {\n memberId: string;\n memberName: string;\n coachId: string;\n coachName: string;\n specialty: string;\n shareWorkouts: boolean;\n shareNutrition: boolean;\n shareMetrics: boolean;\n}\n\nfunction buildEmptyForm(defaultMember: UserSelectValue): CreateCoachingForm {\n return {\n memberId: defaultMember.userId,\n memberName: defaultMember.displayName,\n coachId: '',\n coachName: '',\n specialty: '',\n shareWorkouts: true,\n shareNutrition: true,\n shareMetrics: true,\n };\n}\n\nfunction ShareToggle({\n id,\n label,\n description,\n checked,\n onChange,\n}: {\n id: string;\n label: string;\n description: string;\n checked: boolean;\n onChange: (value: boolean) => void;\n}) {\n return (\n <div className=\"flex items-center justify-between gap-4 rounded-xl bg-bg-sunken/60 p-3\">\n <div className=\"min-w-0\">\n <Label htmlFor={id} className=\"cursor-pointer\">\n {label}\n </Label>\n <p className=\"text-xs text-text-tertiary\">{description}</p>\n </div>\n <Switch id={id} checked={checked} onCheckedChange={onChange} />\n </div>\n );\n}\n\nfunction CreateCoachingDialog({\n open,\n onOpenChange,\n defaultMember,\n}: {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n defaultMember: UserSelectValue;\n}) {\n const create = useMybCreateCoaching();\n const [form, setForm] = useState<CreateCoachingForm>(() => buildEmptyForm(defaultMember));\n const [error, setError] = useState<string | null>(null);\n\n const handleOpenChange = (next: boolean) => {\n if (!next) {\n setForm(buildEmptyForm(defaultMember));\n setError(null);\n }\n onOpenChange(next);\n };\n\n const submit = async () => {\n const coachId = form.coachId.trim();\n const memberId = form.memberId.trim();\n if (!coachId) {\n setError('Coach user id is required.');\n return;\n }\n if (!memberId) {\n setError('Member user id is required.');\n return;\n }\n setError(null);\n const input: MybCreateCoachingInput = {\n coachId,\n memberId,\n specialty: form.specialty.trim() || undefined,\n shareWorkouts: form.shareWorkouts,\n shareNutrition: form.shareNutrition,\n shareMetrics: form.shareMetrics,\n };\n try {\n await create.mutateAsync(input);\n setForm(buildEmptyForm(defaultMember));\n onOpenChange(false);\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to create coaching relationship.');\n }\n };\n\n return (\n <ResponsiveDialog open={open} onOpenChange={handleOpenChange}>\n <ResponsiveDialogContent>\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>New coaching</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n Link a coach and member, and choose exactly what training, nutrition and progress data\n is shared.\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n <form\n className=\"space-y-4\"\n onSubmit={(event) => {\n event.preventDefault();\n void submit();\n }}\n >\n {error && (\n <p className=\"rounded-lg bg-status-error-bg-subtle px-3 py-2 text-sm text-status-error-text\">\n {error}\n </p>\n )}\n <UserSearchSelect\n label=\"Coach\"\n required\n htmlId=\"myb-coaching-coach\"\n userId={form.coachId}\n displayName={form.coachName}\n onSelect={({ userId, displayName }) =>\n setForm((f) => ({ ...f, coachId: userId, coachName: displayName }))\n }\n />\n <UserSearchSelect\n label=\"Member\"\n htmlId=\"myb-coaching-member\"\n userId={form.memberId}\n displayName={form.memberName}\n onSelect={({ userId, displayName }) =>\n setForm((f) => ({ ...f, memberId: userId, memberName: displayName }))\n }\n />\n <div className=\"space-y-2\">\n <Label htmlFor=\"myb-coaching-specialty\">Specialty</Label>\n <Input\n id=\"myb-coaching-specialty\"\n value={form.specialty}\n onChange={(event) => setForm((f) => ({ ...f, specialty: event.target.value }))}\n placeholder=\"e.g., Strength & conditioning\"\n />\n </div>\n <div className=\"space-y-2\">\n <p className=\"text-sm font-medium text-text-primary\">Shared with the coach</p>\n <ShareToggle\n id=\"myb-coaching-share-workouts\"\n label=\"Workouts\"\n description=\"Programs, sessions and logged sets.\"\n checked={form.shareWorkouts}\n onChange={(value) => setForm((f) => ({ ...f, shareWorkouts: value }))}\n />\n <ShareToggle\n id=\"myb-coaching-share-nutrition\"\n label=\"Nutrition\"\n description=\"Plans, meals and daily macros.\"\n checked={form.shareNutrition}\n onChange={(value) => setForm((f) => ({ ...f, shareNutrition: value }))}\n />\n <ShareToggle\n id=\"myb-coaching-share-metrics\"\n label=\"Metrics\"\n description=\"Body metrics and recovery logs.\"\n checked={form.shareMetrics}\n onChange={(value) => setForm((f) => ({ ...f, shareMetrics: value }))}\n />\n </div>\n <ResponsiveDialogFooter>\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={() => handleOpenChange(false)}\n disabled={create.isPending}\n >\n Cancel\n </Button>\n <Button type=\"submit\" disabled={create.isPending}>\n {create.isPending ? 'Creating…' : 'Create coaching'}\n </Button>\n </ResponsiveDialogFooter>\n </form>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n );\n}\n\n// ============================================================================\n// Sharing scope chips\n// ============================================================================\n\nfunction ShareChip({ label, icon, on }: { label: string; icon: typeof Dumbbell; on: boolean }) {\n const Icon = icon;\n const StatusIcon = on ? Check : Minus;\n return (\n <span\n aria-label={`${label} ${on ? 'shared' : 'not shared'}`}\n className={`inline-flex items-center gap-1.5 rounded-full py-0.5 pl-1 pr-2.5 text-xs font-medium ${\n on\n ? 'bg-status-success-bg-subtle text-status-success-text ring-1 ring-inset ring-[color-mix(in_oklab,var(--color-status-success-text)_28%,transparent)]'\n : 'bg-bg-sunken text-text-tertiary ring-1 ring-inset ring-border-seam'\n }`}\n >\n <span\n aria-hidden=\"true\"\n className={`inline-flex size-4 items-center justify-center rounded-full ${\n on\n ? 'bg-[color-mix(in_oklab,var(--color-status-success-text)_22%,transparent)]'\n : 'bg-[color-mix(in_oklab,var(--color-text-tertiary)_18%,transparent)]'\n }`}\n >\n <StatusIcon className=\"h-2.5 w-2.5\" />\n </span>\n <Icon className=\"h-3 w-3\" aria-hidden=\"true\" />\n <span>\n {label} · {on ? 'On' : 'Off'}\n </span>\n </span>\n );\n}\n\n// ============================================================================\n// Coaching card\n// ============================================================================\n\nfunction CoachingCard({\n relationship,\n currentUserId,\n busy,\n onTransition,\n}: {\n relationship: MybCoachingRelationship;\n currentUserId?: string;\n busy: boolean;\n onTransition: (id: string, status: MybCoachingStatus) => void;\n}) {\n const coachIsYou = Boolean(currentUserId) && relationship.coachId === currentUserId;\n const memberIsYou = Boolean(currentUserId) && relationship.memberId === currentUserId;\n const actions = TRANSITIONS[relationship.status];\n const canDiscuss = relationship.status === 'ACTIVE' || relationship.status === 'PAUSED';\n const [showDiscussion, setShowDiscussion] = useState(false);\n const reduce = useReducedMotion();\n\n const isActive = relationship.status === 'ACTIVE';\n const isPending = relationship.status === 'PENDING';\n\n return (\n <GlassCard\n treatment=\"glass\"\n glow={isActive}\n className={\n 'flex h-full flex-col gap-4 p-5 transition-shadow hover:shadow-float' +\n (isPending\n ? ' ring-1 ring-inset ring-[color-mix(in_oklab,var(--color-status-warning-text)_35%,transparent)]'\n : '')\n }\n >\n <div className=\"flex items-start justify-between gap-3\">\n <div className=\"flex min-w-0 items-start gap-3\">\n <IconChip icon={<HeartHandshake className=\"h-4 w-4\" />} />\n <div className=\"min-w-0\">\n <p className=\"truncate text-sm font-semibold text-text-primary\">\n {relationship.specialty?.trim() || 'Coaching relationship'}\n </p>\n <p className=\"text-xs text-text-tertiary\">Coach ↔ member</p>\n </div>\n </div>\n <StatusBadge status={relationship.status} />\n </div>\n\n <div className=\"grid grid-cols-2 gap-3\">\n <div className=\"flex items-center gap-2.5 rounded-lg bg-bg-sunken/60 p-2.5\">\n <span\n aria-hidden=\"true\"\n className={`flex size-8 shrink-0 items-center justify-center rounded-full ring-1 ring-inset ${\n coachIsYou\n ? 'bg-[color-mix(in_oklab,var(--color-action-primary-bg)_16%,transparent)] text-action-primary-bg ring-[color-mix(in_oklab,var(--color-action-primary-bg)_40%,transparent)]'\n : 'bg-bg-surface text-text-tertiary ring-border-seam'\n }`}\n >\n <User className=\"h-4 w-4\" />\n </span>\n <div className=\"min-w-0\">\n <p className=\"text-[11px] font-medium uppercase tracking-wide text-text-tertiary\">\n Coach\n </p>\n <p\n className=\"mt-0.5 truncate text-sm font-medium text-text-primary\"\n title={relationship.coachId}\n >\n {coachIsYou ? 'You' : `#${shortId(relationship.coachId)}`}\n </p>\n </div>\n </div>\n <div className=\"flex items-center gap-2.5 rounded-lg bg-bg-sunken/60 p-2.5\">\n <span\n aria-hidden=\"true\"\n className={`flex size-8 shrink-0 items-center justify-center rounded-full ring-1 ring-inset ${\n memberIsYou\n ? 'bg-[color-mix(in_oklab,var(--color-action-primary-bg)_16%,transparent)] text-action-primary-bg ring-[color-mix(in_oklab,var(--color-action-primary-bg)_40%,transparent)]'\n : 'bg-bg-surface text-text-tertiary ring-border-seam'\n }`}\n >\n <User className=\"h-4 w-4\" />\n </span>\n <div className=\"min-w-0\">\n <p className=\"text-[11px] font-medium uppercase tracking-wide text-text-tertiary\">\n Member\n </p>\n <p\n className=\"mt-0.5 truncate text-sm font-medium text-text-primary\"\n title={relationship.memberId}\n >\n {memberIsYou ? 'You' : `#${shortId(relationship.memberId)}`}\n </p>\n </div>\n </div>\n </div>\n\n <div className=\"flex flex-wrap gap-1.5\">\n <ShareChip label=\"Workouts\" icon={Dumbbell} on={relationship.shareWorkouts} />\n <ShareChip label=\"Nutrition\" icon={Salad} on={relationship.shareNutrition} />\n <ShareChip label=\"Metrics\" icon={LineChart} on={relationship.shareMetrics} />\n </div>\n\n {(relationship.startedAt || relationship.endedAt) && (\n <div className=\"flex flex-wrap items-center gap-x-4 gap-y-1 text-xs text-text-tertiary\">\n {relationship.startedAt && (\n <span className=\"inline-flex items-center gap-1\">\n <CalendarClock className=\"h-3.5 w-3.5\" />\n Started {formatDate(relationship.startedAt)}\n </span>\n )}\n {relationship.endedAt && (\n <span className=\"inline-flex items-center gap-1\">\n <CalendarX className=\"h-3.5 w-3.5\" />\n Ended {formatDate(relationship.endedAt)}\n </span>\n )}\n </div>\n )}\n\n {actions.length > 0 && (\n <div className=\"mt-auto flex flex-wrap gap-2 pt-1\">\n {actions.map((action) => (\n <Button\n key={action.to}\n size=\"sm\"\n variant={action.primary ? undefined : 'outline'}\n disabled={busy}\n onClick={() => onTransition(relationship.id, action.to)}\n >\n {action.label}\n </Button>\n ))}\n </div>\n )}\n\n {canDiscuss && (\n <div\n className={`border-t border-border-seam pt-3 ${actions.length === 0 ? 'mt-auto' : ''}`}\n >\n <button\n type=\"button\"\n onClick={() => setShowDiscussion((prev) => !prev)}\n aria-expanded={showDiscussion}\n className=\"inline-flex items-center gap-1.5 text-sm font-medium text-accent hover:underline\"\n >\n <MessageSquare className=\"h-4 w-4\" />\n {showDiscussion ? 'Hide discussion' : 'Discussion'}\n {showDiscussion ? (\n <ChevronUp className=\"h-4 w-4\" />\n ) : (\n <ChevronDown className=\"h-4 w-4\" />\n )}\n </button>\n <AnimatePresence initial={false}>\n {showDiscussion && (\n <motion.div\n key=\"discussion\"\n initial={reduce ? false : { opacity: 0, height: 0 }}\n animate={{ opacity: 1, height: 'auto' }}\n exit={reduce ? { opacity: 0 } : { opacity: 0, height: 0 }}\n transition={reduce ? { duration: 0 } : { duration: 0.25, ease: MOTION_EASE }}\n className=\"overflow-hidden\"\n >\n <div className=\"mt-3\">\n <MybDiscussion\n entityType=\"mybodyshield-coaching\"\n entityId={relationship.id}\n title=\"Coaching discussion\"\n description=\"A private thread between the coach and member.\"\n />\n </div>\n </motion.div>\n )}\n </AnimatePresence>\n </div>\n )}\n </GlassCard>\n );\n}\n\n// ============================================================================\n// Page\n// ============================================================================\n\nexport function CoachingPage() {\n const { currentUser } = useMyBodyShield();\n const reduce = useReducedMotion();\n const [statusFilter, setStatusFilter] = useState<StatusFilter>('ALL');\n const [createOpen, setCreateOpen] = useState(false);\n const [transitioningId, setTransitioningId] = useState<string | null>(null);\n const [actionError, setActionError] = useState<string | null>(null);\n\n // `useMybCoaching` returns a paginated connection — fetch every relationship\n // once and filter client-side so the tab counts stay accurate across filters.\n const coaching = useMybCoaching();\n const transition = useMybTransitionCoaching();\n\n const all = coaching.data?.items ?? [];\n\n const counts = useMemo(() => {\n const map: Record<MybCoachingStatus, number> = {\n PENDING: 0,\n ACTIVE: 0,\n PAUSED: 0,\n ENDED: 0,\n };\n for (const rel of all) map[rel.status] += 1;\n return map;\n }, [all]);\n\n const statusFilters = useMemo<FilterOption<StatusFilter>[]>(\n () => [\n { value: 'ALL', label: 'All', count: all.length },\n ...FILTER_ORDER.map((status) => ({\n value: status as StatusFilter,\n label: status.charAt(0) + status.slice(1).toLowerCase(),\n count: counts[status],\n })),\n ],\n [all.length, counts]\n );\n\n const rows = useMemo(\n () => (statusFilter === 'ALL' ? all : all.filter((rel) => rel.status === statusFilter)),\n [all, statusFilter]\n );\n\n const handleTransition = async (id: string, status: MybCoachingStatus) => {\n setTransitioningId(id);\n setActionError(null);\n try {\n await transition.mutateAsync({ id, status });\n } catch (err) {\n setActionError(err instanceof Error ? err.message : 'Failed to update the relationship.');\n } finally {\n setTransitioningId(null);\n }\n };\n\n const showLoading = coaching.isLoading && !coaching.data;\n\n return (\n <PageLayout\n title=\"Coaching\"\n description=\"Coach ↔ member relationships and what's shared.\"\n actions={\n <Button onClick={() => setCreateOpen(true)}>\n <Plus className=\"mr-2 h-4 w-4\" />\n New coaching\n </Button>\n }\n >\n <div className=\"mb-6\">\n <FilterTabs options={statusFilters} value={statusFilter} onChange={setStatusFilter} />\n </div>\n\n {actionError && (\n <p className=\"mb-4 rounded-lg bg-status-error-bg-subtle px-3 py-2 text-sm text-status-error-text\">\n {actionError}\n </p>\n )}\n\n {showLoading ? (\n <LoadingView label=\"Loading coaching relationships…\" />\n ) : coaching.isError ? (\n <ErrorView\n message=\"We couldn't load your coaching relationships.\"\n onRetry={() => void coaching.refetch()}\n />\n ) : rows.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n icon={<HeartHandshake className=\"h-10 w-10\" />}\n title={\n all.length === 0 ? 'No coaching relationships' : 'No relationships match this filter'\n }\n description={\n all.length === 0\n ? 'Connect a coach and a member to start sharing training, nutrition and progress data.'\n : 'Try a different status filter, or create a new coaching relationship.'\n }\n action={\n <Button onClick={() => setCreateOpen(true)}>\n <Plus className=\"mr-2 h-4 w-4\" />\n New coaching\n </Button>\n }\n />\n ) : (\n <motion.div\n className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3\"\n variants={reduce ? undefined : STAGGER_CONTAINER}\n initial={reduce ? false : 'hidden'}\n animate=\"show\"\n >\n {rows.map((relationship) => (\n <motion.div\n key={relationship.id}\n className=\"h-full\"\n variants={reduce ? undefined : RISE_ITEM}\n >\n <CoachingCard\n relationship={relationship}\n currentUserId={currentUser?.id}\n busy={transitioningId === relationship.id}\n onTransition={(id, status) => void handleTransition(id, status)}\n />\n </motion.div>\n ))}\n </motion.div>\n )}\n\n <CreateCoachingDialog\n open={createOpen}\n onOpenChange={setCreateOpen}\n defaultMember={{\n userId: currentUser?.id ?? '',\n displayName: currentUser?.name || currentUser?.email || '',\n }}\n />\n </PageLayout>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAgEA,IAAM,IAAoC;CAAC;CAAW;CAAU;CAAU;CAAQ,EAG5E,IAAgD;CAAC;CAAM;CAAG;CAAK;CAAE,EAEjE,IAAoB;CACxB,QAAQ,EAAE;CACV,MAAM,EAAE,YAAY,EAAE,iBAAiB,KAAM,EAAE;CAChD,EACK,IAAY;CAChB,QAAQ;EAAE,SAAS;EAAG,GAAG;EAAI;CAC7B,MAAM;EAAE,SAAS;EAAG,GAAG;EAAG,YAAY;GAAE,UAAU;GAAM,MAAM;GAAa;EAAE;CAC9E,EAGK,IAGF;CACF,SAAS,CAAC;EAAE,IAAI;EAAU,OAAO;EAAU,SAAS;EAAM,CAAC;CAC3D,QAAQ,CACN;EAAE,IAAI;EAAU,OAAO;EAAS,SAAS;EAAO,EAChD;EAAE,IAAI;EAAS,OAAO;EAAO,SAAS;EAAO,CAC9C;CACD,QAAQ,CACN;EAAE,IAAI;EAAU,OAAO;EAAU,SAAS;EAAM,EAChD;EAAE,IAAI;EAAS,OAAO;EAAO,SAAS;EAAO,CAC9C;CACD,OAAO,EAAE;CACV;AAGD,SAAS,EAAQ,GAAoB;AACnC,QAAO,EAAG,UAAU,KAAK,IAAK,GAAG,EAAG,MAAM,GAAG,EAAE,CAAC;;AAkBlD,SAAS,EAAe,GAAoD;AAC1E,QAAO;EACL,UAAU,EAAc;EACxB,YAAY,EAAc;EAC1B,SAAS;EACT,WAAW;EACX,WAAW;EACX,eAAe;EACf,gBAAgB;EAChB,cAAc;EACf;;AAGH,SAAS,EAAY,EACnB,OACA,UACA,gBACA,YACA,eAOC;AACD,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf,CACE,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,GAAD;IAAO,SAAS;IAAI,WAAU;cAC3B;IACK,CAAA,EACR,kBAAC,KAAD;IAAG,WAAU;cAA8B;IAAgB,CAAA,CACvD;MACN,kBAAC,GAAD;GAAY;GAAa;GAAS,iBAAiB;GAAY,CAAA,CAC3D;;;AAIV,SAAS,GAAqB,EAC5B,SACA,iBACA,oBAKC;CACD,IAAM,IAAS,GAAsB,EAC/B,CAAC,GAAM,KAAW,QAAmC,EAAe,EAAc,CAAC,EACnF,CAAC,GAAO,KAAY,EAAwB,KAAK,EAEjD,KAAoB,MAAkB;AAK1C,EAJK,MACH,EAAQ,EAAe,EAAc,CAAC,EACtC,EAAS,KAAK,GAEhB,EAAa,EAAK;IAGd,IAAS,YAAY;EACzB,IAAM,IAAU,EAAK,QAAQ,MAAM,EAC7B,IAAW,EAAK,SAAS,MAAM;AACrC,MAAI,CAAC,GAAS;AACZ,KAAS,6BAA6B;AACtC;;AAEF,MAAI,CAAC,GAAU;AACb,KAAS,8BAA8B;AACvC;;AAEF,IAAS,KAAK;EACd,IAAM,IAAgC;GACpC;GACA;GACA,WAAW,EAAK,UAAU,MAAM,IAAI,KAAA;GACpC,eAAe,EAAK;GACpB,gBAAgB,EAAK;GACrB,cAAc,EAAK;GACpB;AACD,MAAI;AAGF,GAFA,MAAM,EAAO,YAAY,EAAM,EAC/B,EAAQ,EAAe,EAAc,CAAC,EACtC,EAAa,GAAM;WACZ,GAAK;AACZ,KAAS,aAAe,QAAQ,EAAI,UAAU,0CAA0C;;;AAI5F,QACE,kBAAC,GAAD;EAAwB;EAAM,cAAc;YAC1C,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD,EAAA,UAAuB,gBAAoC,CAAA,EAC3D,kBAAC,GAAD,EAAA,UAA6B,qGAGC,CAAA,CACP,EAAA,CAAA,EACzB,kBAAC,QAAD;GACE,WAAU;GACV,WAAW,MAAU;AAEd,IADL,EAAM,gBAAgB,EACjB,GAAQ;;aAJjB;IAOG,KACC,kBAAC,KAAD;KAAG,WAAU;eACV;KACC,CAAA;IAEN,kBAAC,GAAD;KACE,OAAM;KACN,UAAA;KACA,QAAO;KACP,QAAQ,EAAK;KACb,aAAa,EAAK;KAClB,WAAW,EAAE,WAAQ,qBACnB,GAAS,OAAO;MAAE,GAAG;MAAG,SAAS;MAAQ,WAAW;MAAa,EAAE;KAErE,CAAA;IACF,kBAAC,GAAD;KACE,OAAM;KACN,QAAO;KACP,QAAQ,EAAK;KACb,aAAa,EAAK;KAClB,WAAW,EAAE,WAAQ,qBACnB,GAAS,OAAO;MAAE,GAAG;MAAG,UAAU;MAAQ,YAAY;MAAa,EAAE;KAEvE,CAAA;IACF,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,GAAD;MAAO,SAAQ;gBAAyB;MAAiB,CAAA,EACzD,kBAAC,GAAD;MACE,IAAG;MACH,OAAO,EAAK;MACZ,WAAW,MAAU,GAAS,OAAO;OAAE,GAAG;OAAG,WAAW,EAAM,OAAO;OAAO,EAAE;MAC9E,aAAY;MACZ,CAAA,CACE;;IACN,kBAAC,OAAD;KAAK,WAAU;eAAf;MACE,kBAAC,KAAD;OAAG,WAAU;iBAAwC;OAAyB,CAAA;MAC9E,kBAAC,GAAD;OACE,IAAG;OACH,OAAM;OACN,aAAY;OACZ,SAAS,EAAK;OACd,WAAW,MAAU,GAAS,OAAO;QAAE,GAAG;QAAG,eAAe;QAAO,EAAE;OACrE,CAAA;MACF,kBAAC,GAAD;OACE,IAAG;OACH,OAAM;OACN,aAAY;OACZ,SAAS,EAAK;OACd,WAAW,MAAU,GAAS,OAAO;QAAE,GAAG;QAAG,gBAAgB;QAAO,EAAE;OACtE,CAAA;MACF,kBAAC,GAAD;OACE,IAAG;OACH,OAAM;OACN,aAAY;OACZ,SAAS,EAAK;OACd,WAAW,MAAU,GAAS,OAAO;QAAE,GAAG;QAAG,cAAc;QAAO,EAAE;OACpE,CAAA;MACE;;IACN,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD;KACE,MAAK;KACL,SAAQ;KACR,eAAe,EAAiB,GAAM;KACtC,UAAU,EAAO;eAClB;KAEQ,CAAA,EACT,kBAAC,GAAD;KAAQ,MAAK;KAAS,UAAU,EAAO;eACpC,EAAO,YAAY,cAAc;KAC3B,CAAA,CACc,EAAA,CAAA;IACpB;KACiB,EAAA,CAAA;EACT,CAAA;;AAQvB,SAAS,EAAU,EAAE,UAAO,SAAM,SAA6D;CAC7F,IAAM,IAAO,GACP,IAAa,IAAK,IAAQ;AAChC,QACE,kBAAC,QAAD;EACE,cAAY,GAAG,EAAM,GAAG,IAAK,WAAW;EACxC,WAAW,wFACT,IACI,uJACA;YALR;GAQE,kBAAC,QAAD;IACE,eAAY;IACZ,WAAW,+DACT,IACI,8EACA;cAGN,kBAAC,GAAD,EAAY,WAAU,eAAgB,CAAA;IACjC,CAAA;GACP,kBAAC,GAAD;IAAM,WAAU;IAAU,eAAY;IAAS,CAAA;GAC/C,kBAAC,QAAD,EAAA,UAAA;IACG;IAAM;IAAI,IAAK,OAAO;IAClB,EAAA,CAAA;GACF;;;AAQX,SAAS,GAAa,EACpB,iBACA,kBACA,SACA,mBAMC;CACD,IAAM,IAAa,EAAQ,KAAkB,EAAa,YAAY,GAChE,IAAc,EAAQ,KAAkB,EAAa,aAAa,GAClE,IAAU,EAAY,EAAa,SACnC,IAAa,EAAa,WAAW,YAAY,EAAa,WAAW,UACzE,CAAC,GAAgB,KAAqB,EAAS,GAAM,EACrD,IAAS,GAAkB;AAKjC,QACE,kBAAC,GAAD;EACE,WAAU;EACV,MANa,EAAa,WAAW;EAOrC,WACE,yEAPY,EAAa,WAAW,YAShC,mGACA;YAPR;GAUE,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,GAAD,EAAU,MAAM,kBAAC,GAAD,EAAgB,WAAU,WAAY,CAAA,EAAI,CAAA,EAC1D,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,KAAD;OAAG,WAAU;iBACV,EAAa,WAAW,MAAM,IAAI;OACjC,CAAA,EACJ,kBAAC,KAAD;OAAG,WAAU;iBAA6B;OAAkB,CAAA,CACxD;QACF;QACN,kBAAC,GAAD,EAAa,QAAQ,EAAa,QAAU,CAAA,CACxC;;GAEN,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,QAAD;MACE,eAAY;MACZ,WAAW,mFACT,IACI,6KACA;gBAGN,kBAAC,GAAD,EAAM,WAAU,WAAY,CAAA;MACvB,CAAA,EACP,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,KAAD;OAAG,WAAU;iBAAqE;OAE9E,CAAA,EACJ,kBAAC,KAAD;OACE,WAAU;OACV,OAAO,EAAa;iBAEnB,IAAa,QAAQ,IAAI,EAAQ,EAAa,QAAQ;OACrD,CAAA,CACA;QACF;QACN,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,QAAD;MACE,eAAY;MACZ,WAAW,mFACT,IACI,6KACA;gBAGN,kBAAC,GAAD,EAAM,WAAU,WAAY,CAAA;MACvB,CAAA,EACP,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,KAAD;OAAG,WAAU;iBAAqE;OAE9E,CAAA,EACJ,kBAAC,KAAD;OACE,WAAU;OACV,OAAO,EAAa;iBAEnB,IAAc,QAAQ,IAAI,EAAQ,EAAa,SAAS;OACvD,CAAA,CACA;QACF;OACF;;GAEN,kBAAC,OAAD;IAAK,WAAU;cAAf;KACE,kBAAC,GAAD;MAAW,OAAM;MAAW,MAAM;MAAU,IAAI,EAAa;MAAiB,CAAA;KAC9E,kBAAC,GAAD;MAAW,OAAM;MAAY,MAAM;MAAO,IAAI,EAAa;MAAkB,CAAA;KAC7E,kBAAC,GAAD;MAAW,OAAM;MAAU,MAAM;MAAW,IAAI,EAAa;MAAgB,CAAA;KACzE;;IAEJ,EAAa,aAAa,EAAa,YACvC,kBAAC,OAAD;IAAK,WAAU;cAAf,CACG,EAAa,aACZ,kBAAC,QAAD;KAAM,WAAU;eAAhB;MACE,kBAAC,GAAD,EAAe,WAAU,eAAgB,CAAA;;MAChC,EAAW,EAAa,UAAU;MACtC;QAER,EAAa,WACZ,kBAAC,QAAD;KAAM,WAAU;eAAhB;MACE,kBAAC,GAAD,EAAW,WAAU,eAAgB,CAAA;;MAC9B,EAAW,EAAa,QAAQ;MAClC;OAEL;;GAGP,EAAQ,SAAS,KAChB,kBAAC,OAAD;IAAK,WAAU;cACZ,EAAQ,KAAK,MACZ,kBAAC,GAAD;KAEE,MAAK;KACL,SAAS,EAAO,UAAU,KAAA,IAAY;KACtC,UAAU;KACV,eAAe,EAAa,EAAa,IAAI,EAAO,GAAG;eAEtD,EAAO;KACD,EAPF,EAAO,GAOL,CACT;IACE,CAAA;GAGP,KACC,kBAAC,OAAD;IACE,WAAW,oCAAoC,EAAQ,WAAW,IAAI,YAAY;cADpF,CAGE,kBAAC,UAAD;KACE,MAAK;KACL,eAAe,GAAmB,MAAS,CAAC,EAAK;KACjD,iBAAe;KACf,WAAU;eAJZ;MAME,kBAAC,GAAD,EAAe,WAAU,WAAY,CAAA;MACpC,IAAiB,oBAAoB;MAEpC,EADD,IACE,IAEA,GAFD,EAAW,WAAU,WAAY,CAEE;MAE9B;QACT,kBAAC,GAAD;KAAiB,SAAS;eACvB,KACC,kBAAC,EAAO,KAAR;MAEE,SAAS,IAAS,KAAQ;OAAE,SAAS;OAAG,QAAQ;OAAG;MACnD,SAAS;OAAE,SAAS;OAAG,QAAQ;OAAQ;MACvC,MAAM,IAAS,EAAE,SAAS,GAAG,GAAG;OAAE,SAAS;OAAG,QAAQ;OAAG;MACzD,YAAY,IAAS,EAAE,UAAU,GAAG,GAAG;OAAE,UAAU;OAAM,MAAM;OAAa;MAC5E,WAAU;gBAEV,kBAAC,OAAD;OAAK,WAAU;iBACb,kBAAC,GAAD;QACE,YAAW;QACX,UAAU,EAAa;QACvB,OAAM;QACN,aAAY;QACZ,CAAA;OACE,CAAA;MACK,EAfP,aAeO;KAEC,CAAA,CACd;;GAEE;;;AAQhB,SAAgB,KAAe;CAC7B,IAAM,EAAE,mBAAgB,GAAiB,EACnC,IAAS,GAAkB,EAC3B,CAAC,GAAc,KAAmB,EAAuB,MAAM,EAC/D,CAAC,GAAY,KAAiB,EAAS,GAAM,EAC7C,CAAC,GAAiB,KAAsB,EAAwB,KAAK,EACrE,CAAC,GAAa,KAAkB,EAAwB,KAAK,EAI7D,IAAW,GAAgB,EAC3B,IAAa,GAA0B,EAEvC,IAAM,EAAS,MAAM,SAAS,EAAE,EAEhC,IAAS,QAAc;EAC3B,IAAM,IAAyC;GAC7C,SAAS;GACT,QAAQ;GACR,QAAQ;GACR,OAAO;GACR;AACD,OAAK,IAAM,KAAO,EAAK,GAAI,EAAI,WAAW;AAC1C,SAAO;IACN,CAAC,EAAI,CAAC,EAEH,IAAgB,QACd,CACJ;EAAE,OAAO;EAAO,OAAO;EAAO,OAAO,EAAI;EAAQ,EACjD,GAAG,EAAa,KAAK,OAAY;EAC/B,OAAO;EACP,OAAO,EAAO,OAAO,EAAE,GAAG,EAAO,MAAM,EAAE,CAAC,aAAa;EACvD,OAAO,EAAO;EACf,EAAE,CACJ,EACD,CAAC,EAAI,QAAQ,EAAO,CACrB,EAEK,IAAO,QACJ,MAAiB,QAAQ,IAAM,EAAI,QAAQ,MAAQ,EAAI,WAAW,EAAa,EACtF,CAAC,GAAK,EAAa,CACpB,EAEK,IAAmB,OAAO,GAAY,MAA8B;AAExE,EADA,EAAmB,EAAG,EACtB,EAAe,KAAK;AACpB,MAAI;AACF,SAAM,EAAW,YAAY;IAAE;IAAI;IAAQ,CAAC;WACrC,GAAK;AACZ,KAAe,aAAe,QAAQ,EAAI,UAAU,qCAAqC;YACjF;AACR,KAAmB,KAAK;;IAItB,IAAc,EAAS,aAAa,CAAC,EAAS;AAEpD,QACE,kBAAC,GAAD;EACE,OAAM;EACN,aAAY;EACZ,SACE,kBAAC,GAAD;GAAQ,eAAe,EAAc,GAAK;aAA1C,CACE,kBAAC,GAAD,EAAM,WAAU,gBAAiB,CAAA,EAAA,eAE1B;;YAPb;GAUE,kBAAC,OAAD;IAAK,WAAU;cACb,kBAAC,GAAD;KAAY,SAAS;KAAe,OAAO;KAAc,UAAU;KAAmB,CAAA;IAClF,CAAA;GAEL,KACC,kBAAC,KAAD;IAAG,WAAU;cACV;IACC,CAAA;GAGL,IACC,kBAAC,GAAD,EAAa,OAAM,mCAAoC,CAAA,GACrD,EAAS,UACX,kBAAC,GAAD;IACE,SAAQ;IACR,eAAe,KAAK,EAAS,SAAS;IACtC,CAAA,GACA,EAAK,WAAW,IAClB,kBAAC,GAAD;IACE,cAAa;IACb,MAAM,kBAAC,GAAD,EAAgB,WAAU,aAAc,CAAA;IAC9C,OACE,EAAI,WAAW,IAAI,8BAA8B;IAEnD,aACE,EAAI,WAAW,IACX,yFACA;IAEN,QACE,kBAAC,GAAD;KAAQ,eAAe,EAAc,GAAK;eAA1C,CACE,kBAAC,GAAD,EAAM,WAAU,gBAAiB,CAAA,EAAA,eAE1B;;IAEX,CAAA,GAEF,kBAAC,EAAO,KAAR;IACE,WAAU;IACV,UAAU,IAAS,KAAA,IAAY;IAC/B,SAAS,IAAS,KAAQ;IAC1B,SAAQ;cAEP,EAAK,KAAK,MACT,kBAAC,EAAO,KAAR;KAEE,WAAU;KACV,UAAU,IAAS,KAAA,IAAY;eAE/B,kBAAC,IAAD;MACgB;MACd,eAAe,GAAa;MAC5B,MAAM,MAAoB,EAAa;MACvC,eAAe,GAAI,MAAW,KAAK,EAAiB,GAAI,EAAO;MAC/D,CAAA;KACS,EAVN,EAAa,GAUP,CACb;IACS,CAAA;GAGf,kBAAC,IAAD;IACE,MAAM;IACN,cAAc;IACd,eAAe;KACb,QAAQ,GAAa,MAAM;KAC3B,aAAa,GAAa,QAAQ,GAAa,SAAS;KACzD;IACD,CAAA;GACS"}
@@ -71,7 +71,7 @@ function j() {
71
71
  damping: 22
72
72
  }
73
73
  } } : {}
74
- }, V = Te.data ?? [], H = Ee.data ?? [], U = De.data ?? [], W = L.data ?? [], G = m(() => [...V].sort((e, t) => new Date(t.loggedAt).getTime() - new Date(e.loggedAt).getTime())[0], [V]), K = m(() => [...U].sort((e, t) => new Date(t.loggedAt).getTime() - new Date(e.loggedAt).getTime())[0], [U]), q = m(() => H.length === 0 ? null : H.reduce((e, t) => e + t.sleepHours, 0) / H.length, [H]), J = k === "SLEEP", Y = k === "ALL" ? "All metrics" : d[k].label, X = m(() => [...W].sort((e, t) => new Date(e.loggedAt).getTime() - new Date(t.loggedAt).getTime()).map((e) => ({
74
+ }, V = Te.data?.items ?? [], H = Ee.data?.items ?? [], U = De.data?.items ?? [], W = L.data?.items ?? [], G = m(() => [...V].sort((e, t) => new Date(t.loggedAt).getTime() - new Date(e.loggedAt).getTime())[0], [V]), K = m(() => [...U].sort((e, t) => new Date(t.loggedAt).getTime() - new Date(e.loggedAt).getTime())[0], [U]), q = m(() => H.length === 0 ? null : H.reduce((e, t) => e + t.sleepHours, 0) / H.length, [H]), J = k === "SLEEP", Y = k === "ALL" ? "All metrics" : d[k].label, X = m(() => [...W].sort((e, t) => new Date(e.loggedAt).getTime() - new Date(t.loggedAt).getTime()).map((e) => ({
75
75
  label: n(e.loggedAt),
76
76
  value: J ? e.sleepHours : e.score
77
77
  })), [W, J]), Z = X.length > 0 ? X[X.length - 1] : void 0, Q = (e = "READINESS") => {
@@ -1 +1 @@
1
- {"version":3,"file":"RecoveryPage.js","names":[],"sources":["../../../../src/mybodyshield/pages/recovery/RecoveryPage.tsx"],"sourcesContent":["/**\n * Recovery — sleep, readiness, soreness, stress & mood tracking.\n * @module mybodyshield/pages/recovery\n *\n * Reads the real `mybListRecoveryLogs` array endpoint (no pagination). KPIs pull\n * the readiness / sleep / mood metrics independently so they stay stable while\n * the FilterTabs drive the charted + tabulated metric. New entries go through\n * `mybLogRecovery`. No mock data — everything is derived from the hooks.\n */\n\nimport { useMemo, useState } from 'react';\nimport { Plus, HeartPulse } from 'lucide-react';\nimport {\n ResponsiveContainer,\n AreaChart,\n Area,\n XAxis,\n YAxis,\n CartesianGrid,\n Tooltip,\n ReferenceDot,\n} from 'recharts';\nimport { motion, useReducedMotion, type MotionProps } from 'framer-motion';\nimport {\n Button,\n Input,\n Label,\n Textarea,\n Select,\n SelectTrigger,\n SelectContent,\n SelectItem,\n SelectValue,\n GlassCard,\n Spinner,\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n ResponsiveDialogDescription,\n ResponsiveDialogFooter,\n} from '@burdenoff/fe-libs/ui';\nimport {\n PageLayout,\n MetricsCard,\n FilterTabs,\n DataTable,\n LoadingView,\n ErrorView,\n Section,\n EmptyState,\n type Column,\n type FilterOption,\n} from '../../components';\nimport { useMybRecoveryLogs, useMybLogRecovery } from '../../hooks';\nimport type { MybRecoveryMetric, MybRecoveryLog, MybLogRecoveryInput } from '../../types';\nimport { formatNumber, formatDate, formatRelativeTime } from '../../utils/formatters';\nimport { RECOVERY_META, IconChip } from '../_shared/visuals';\n\ntype RecoveryFilter = 'ALL' | MybRecoveryMetric;\n\ntype Accent = 'success' | 'warning' | 'error';\n\nconst RECOVERY_METRICS: MybRecoveryMetric[] = ['SLEEP', 'READINESS', 'SORENESS', 'STRESS', 'MOOD'];\n\nconst FILTER_OPTIONS: FilterOption<RecoveryFilter>[] = [\n { value: 'ALL', label: 'All' },\n ...RECOVERY_METRICS.map((m) => ({ value: m, label: RECOVERY_META[m].label })),\n];\n\n// Brand emerald accent — dominant color for the recovery area chart & endpoint.\nconst CHART_ACCENT = 'var(--color-action-primary-bg)';\n\nconst scoreAccent = (score: number): Accent =>\n score >= 70 ? 'success' : score >= 40 ? 'warning' : 'error';\n\nconst sleepAccent = (hours: number): Accent =>\n hours >= 7 ? 'success' : hours >= 6 ? 'warning' : 'error';\n\n/** Format a Date into a `datetime-local` input value (local time, minute precision). */\nfunction toDatetimeLocalValue(date: Date): string {\n const pad = (n: number): string => String(n).padStart(2, '0');\n return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}T${pad(\n date.getHours()\n )}:${pad(date.getMinutes())}`;\n}\n\ninterface RecoveryForm {\n metric: MybRecoveryMetric;\n score: string;\n sleepHours: string;\n notes: string;\n loggedAt: string;\n}\n\nconst makeEmptyForm = (metric: MybRecoveryMetric = 'READINESS'): RecoveryForm => ({\n metric,\n score: '',\n sleepHours: '',\n notes: '',\n loggedAt: toDatetimeLocalValue(new Date()),\n});\n\nexport function RecoveryPage() {\n const [filter, setFilter] = useState<RecoveryFilter>('ALL');\n const [open, setOpen] = useState(false);\n const [form, setForm] = useState<RecoveryForm>(makeEmptyForm);\n const [err, setErr] = useState<string | null>(null);\n\n // KPI feeds — independent of the active filter so the header stays stable.\n const readiness = useMybRecoveryLogs('READINESS');\n const sleep = useMybRecoveryLogs('SLEEP');\n const mood = useMybRecoveryLogs('MOOD');\n\n // Charted + tabulated metric (undefined → all metrics).\n const selectedMetric = filter === 'ALL' ? undefined : filter;\n const logs = useMybRecoveryLogs(selectedMetric);\n\n const logRecovery = useMybLogRecovery();\n\n const reduced = useReducedMotion();\n const rise = (delay: number, hover = true): MotionProps =>\n reduced\n ? { initial: false }\n : {\n initial: { opacity: 0, y: 14 },\n animate: { opacity: 1, y: 0 },\n transition: { duration: 0.45, delay, ease: 'easeOut' },\n ...(hover\n ? {\n whileHover: {\n y: -3,\n transition: { type: 'spring', stiffness: 300, damping: 22 },\n },\n }\n : {}),\n };\n\n const readinessRows = readiness.data ?? [];\n const sleepRows = sleep.data ?? [];\n const moodRows = mood.data ?? [];\n const logRows = logs.data ?? [];\n\n const latestReadiness = useMemo(\n () =>\n [...readinessRows].sort(\n (a, b) => new Date(b.loggedAt).getTime() - new Date(a.loggedAt).getTime()\n )[0],\n [readinessRows]\n );\n\n const latestMood = useMemo(\n () =>\n [...moodRows].sort(\n (a, b) => new Date(b.loggedAt).getTime() - new Date(a.loggedAt).getTime()\n )[0],\n [moodRows]\n );\n\n const avgSleep = useMemo(() => {\n if (sleepRows.length === 0) return null;\n const sum = sleepRows.reduce((acc, l) => acc + l.sleepHours, 0);\n return sum / sleepRows.length;\n }, [sleepRows]);\n\n const isSleepSelected = filter === 'SLEEP';\n const selectedLabel = filter === 'ALL' ? 'All metrics' : RECOVERY_META[filter].label;\n\n const chartData = useMemo(\n () =>\n [...logRows]\n .sort((a, b) => new Date(a.loggedAt).getTime() - new Date(b.loggedAt).getTime())\n .map((l) => ({\n label: formatDate(l.loggedAt),\n value: isSleepSelected ? l.sleepHours : l.score,\n })),\n [logRows, isSleepSelected]\n );\n const lastPoint = chartData.length > 0 ? chartData[chartData.length - 1] : undefined;\n\n const openLog = (metric: MybRecoveryMetric = 'READINESS') => {\n setForm(makeEmptyForm(metric));\n setErr(null);\n setOpen(true);\n };\n\n const isSleepForm = form.metric === 'SLEEP';\n\n const submit = async () => {\n setErr(null);\n if (isSleepForm) {\n if (form.sleepHours.trim() === '') {\n setErr('Sleep hours is required for sleep entries.');\n return;\n }\n const h = Number(form.sleepHours);\n if (!Number.isFinite(h) || h < 0 || h > 24) {\n setErr('Enter sleep hours between 0 and 24.');\n return;\n }\n } else {\n if (form.score.trim() === '') {\n setErr('A score is required.');\n return;\n }\n }\n if (form.score.trim() !== '') {\n const s = Number(form.score);\n if (!Number.isFinite(s) || s < 0 || s > 100) {\n setErr('Enter a score between 0 and 100.');\n return;\n }\n }\n\n const input: MybLogRecoveryInput = {\n metric: form.metric,\n score: form.score.trim() !== '' ? Number(form.score) : undefined,\n sleepHours: form.sleepHours.trim() !== '' ? Number(form.sleepHours) : undefined,\n notes: form.notes.trim() || undefined,\n loggedAt: form.loggedAt ? new Date(form.loggedAt).toISOString() : undefined,\n };\n\n try {\n await logRecovery.mutateAsync(input);\n setOpen(false);\n } catch (e) {\n setErr(e instanceof Error ? e.message : 'Failed to log recovery.');\n }\n };\n\n const columns: Column<MybRecoveryLog>[] = [\n {\n key: 'metric',\n header: 'Metric',\n sortValue: (r) => RECOVERY_META[r.metric].label,\n render: (r) => {\n const meta = RECOVERY_META[r.metric];\n const Icon = meta.icon;\n return (\n <span className=\"inline-flex items-center gap-2.5\">\n <IconChip icon={<Icon className=\"h-4 w-4\" />} tint={meta.tint} />\n <span className=\"font-medium text-text-primary\">{meta.label}</span>\n </span>\n );\n },\n },\n {\n key: 'value',\n header: 'Value',\n align: 'right',\n sortValue: (r) => (r.metric === 'SLEEP' ? r.sleepHours : r.score),\n render: (r) =>\n r.metric === 'SLEEP' ? (\n <span className=\"tabular-nums text-text-primary\">\n {formatNumber(r.sleepHours, { maximumFractionDigits: 1 })} h\n </span>\n ) : (\n <span className=\"tabular-nums text-text-primary\">{formatNumber(r.score)}</span>\n ),\n },\n {\n key: 'notes',\n header: 'Notes',\n hideOnMobile: true,\n render: (r) =>\n r.notes && r.notes.trim() ? (\n <span className=\"text-text-secondary\">{r.notes}</span>\n ) : (\n <span className=\"text-text-tertiary\">—</span>\n ),\n },\n {\n key: 'loggedAt',\n header: 'Logged',\n align: 'right',\n sortValue: (r) => new Date(r.loggedAt).getTime(),\n render: (r) => (\n <span className=\"whitespace-nowrap text-text-tertiary\">\n {formatRelativeTime(r.loggedAt)}\n </span>\n ),\n },\n ];\n\n const ReadinessIcon = RECOVERY_META.READINESS.icon;\n const SleepIcon = RECOVERY_META.SLEEP.icon;\n const MoodIcon = RECOVERY_META.MOOD.icon;\n\n return (\n <PageLayout\n title=\"Recovery\"\n description=\"Sleep, readiness, soreness, stress & mood.\"\n actions={\n <Button className=\"gap-2\" onClick={() => openLog()}>\n <Plus className=\"h-4 w-4\" />\n Log recovery\n </Button>\n }\n >\n {/* KPI grid */}\n <motion.div {...rise(0)} className=\"grid grid-cols-1 gap-4 sm:grid-cols-3\">\n <MetricsCard\n icon={\n <IconChip\n icon={<ReadinessIcon className=\"h-5 w-5\" />}\n tint={RECOVERY_META.READINESS.tint}\n />\n }\n value={latestReadiness ? formatNumber(latestReadiness.score) : '—'}\n label=\"Latest readiness\"\n accent={latestReadiness ? scoreAccent(latestReadiness.score) : undefined}\n footer={\n latestReadiness ? (\n <p className=\"text-xs text-text-tertiary\">\n {formatRelativeTime(latestReadiness.loggedAt)}\n </p>\n ) : (\n <button\n type=\"button\"\n onClick={() => openLog('READINESS')}\n className=\"text-xs font-medium text-accent hover:underline\"\n >\n Log readiness →\n </button>\n )\n }\n />\n <MetricsCard\n icon={\n <IconChip icon={<SleepIcon className=\"h-5 w-5\" />} tint={RECOVERY_META.SLEEP.tint} />\n }\n value={\n avgSleep != null ? `${formatNumber(avgSleep, { maximumFractionDigits: 1 })} h` : '—'\n }\n label=\"Avg sleep\"\n accent={avgSleep != null ? sleepAccent(avgSleep) : undefined}\n footer={\n sleepRows.length > 0 ? (\n <p className=\"text-xs text-text-tertiary\">\n <span className=\"tabular-nums text-text-secondary\">{sleepRows.length}</span>{' '}\n {sleepRows.length === 1 ? 'night' : 'nights'} logged\n </p>\n ) : (\n <button\n type=\"button\"\n onClick={() => openLog('SLEEP')}\n className=\"text-xs font-medium text-accent hover:underline\"\n >\n Log sleep →\n </button>\n )\n }\n />\n <MetricsCard\n icon={<IconChip icon={<MoodIcon className=\"h-5 w-5\" />} tint={RECOVERY_META.MOOD.tint} />}\n value={latestMood ? formatNumber(latestMood.score) : '—'}\n label=\"Latest mood\"\n accent={latestMood ? scoreAccent(latestMood.score) : undefined}\n footer={\n latestMood ? (\n <p className=\"text-xs text-text-tertiary\">\n {formatRelativeTime(latestMood.loggedAt)}\n </p>\n ) : (\n <button\n type=\"button\"\n onClick={() => openLog('MOOD')}\n className=\"text-xs font-medium text-accent hover:underline\"\n >\n Log mood →\n </button>\n )\n }\n />\n </motion.div>\n\n {/* Metric filter */}\n <div className=\"mt-6 flex flex-wrap items-center gap-3\">\n <FilterTabs options={FILTER_OPTIONS} value={filter} onChange={setFilter} />\n </div>\n\n {/* Trend chart */}\n <Section\n title={`${selectedLabel} trend`}\n description={isSleepSelected ? 'Sleep hours over time.' : 'Recovery score over time.'}\n >\n <motion.div {...rise(0.08)}>\n <GlassCard\n treatment=\"glass\"\n className=\"rounded-2xl p-6 transition-shadow duration-300 hover:shadow-2xl\"\n >\n {logs.isLoading ? (\n <div className=\"flex h-[280px] items-center justify-center\">\n <Spinner size=\"lg\" />\n </div>\n ) : logs.isError ? (\n <ErrorView\n message=\"We couldn't load this metric's history.\"\n onRetry={() => logs.refetch()}\n />\n ) : chartData.length === 0 ? (\n <div className=\"flex h-[280px] flex-col items-center justify-center text-center\">\n <span className=\"flex size-11 items-center justify-center rounded-full bg-[var(--color-accent-soft)]\">\n <HeartPulse className=\"h-5 w-5 text-accent\" />\n </span>\n <p className=\"mt-3 text-sm font-medium text-text-primary\">Nothing to chart yet</p>\n <p className=\"mt-1 max-w-xs text-xs text-text-tertiary\">\n Log a few {selectedLabel.toLowerCase()} entries to see your trend here.\n </p>\n </div>\n ) : (\n <ResponsiveContainer width=\"100%\" height={280}>\n <AreaChart data={chartData} margin={{ top: 12, right: 16, bottom: 4, left: 4 }}>\n <defs>\n <linearGradient id=\"myb-recovery-fill\" x1=\"0\" y1=\"0\" x2=\"0\" y2=\"1\">\n <stop offset=\"0%\" stopColor={CHART_ACCENT} stopOpacity={0.38} />\n <stop offset=\"55%\" stopColor={CHART_ACCENT} stopOpacity={0.12} />\n <stop offset=\"100%\" stopColor={CHART_ACCENT} stopOpacity={0} />\n </linearGradient>\n </defs>\n <CartesianGrid\n vertical={false}\n stroke=\"var(--color-border-seam)\"\n strokeDasharray=\"4 4\"\n />\n <XAxis\n dataKey=\"label\"\n tickLine={false}\n axisLine={false}\n tickMargin={8}\n minTickGap={20}\n tick={{ fill: 'var(--color-text-tertiary)', fontSize: 12 }}\n />\n <YAxis\n width={44}\n tickLine={false}\n axisLine={false}\n tick={{ fill: 'var(--color-text-tertiary)', fontSize: 12 }}\n />\n <Tooltip\n contentStyle={{\n background: 'var(--color-bg-surface)',\n border: '1px solid var(--color-border-seam)',\n borderRadius: 12,\n color: 'var(--color-text-primary)',\n fontSize: 12,\n }}\n labelStyle={{ color: 'var(--color-text-tertiary)' }}\n cursor={{ stroke: 'var(--color-border-seam)', strokeDasharray: '4 4' }}\n />\n <Area\n type=\"monotone\"\n dataKey=\"value\"\n name={isSleepSelected ? 'Sleep hours' : 'Score'}\n stroke={CHART_ACCENT}\n strokeWidth={2.5}\n fill=\"url(#myb-recovery-fill)\"\n fillOpacity={1}\n dot={false}\n activeDot={{\n r: 4,\n strokeWidth: 2,\n stroke: 'var(--color-bg-surface)',\n fill: CHART_ACCENT,\n }}\n isAnimationActive={!reduced}\n animationDuration={900}\n />\n {lastPoint && (\n <ReferenceDot\n x={lastPoint.label}\n y={lastPoint.value}\n r={12}\n isFront\n ifOverflow=\"visible\"\n fill={CHART_ACCENT}\n fillOpacity={0.16}\n stroke=\"none\"\n />\n )}\n {lastPoint && (\n <ReferenceDot\n x={lastPoint.label}\n y={lastPoint.value}\n r={5}\n isFront\n ifOverflow=\"visible\"\n fill={CHART_ACCENT}\n stroke=\"var(--color-bg-surface)\"\n strokeWidth={2}\n />\n )}\n </AreaChart>\n </ResponsiveContainer>\n )}\n </GlassCard>\n </motion.div>\n </Section>\n\n {/* Log table */}\n <motion.div {...rise(0.16, false)}>\n <Section title=\"Recovery log\" description=\"Every recovery entry, most recent first.\">\n {logs.isLoading ? (\n <LoadingView label=\"Loading recovery logs…\" />\n ) : logs.isError ? (\n <ErrorView\n message=\"We couldn't load your recovery logs.\"\n onRetry={() => logs.refetch()}\n />\n ) : logRows.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n icon={<HeartPulse className=\"h-10 w-10\" />}\n title=\"No recovery logs yet\"\n description=\"Track your sleep, readiness, soreness, stress or mood to see how your body is recovering between sessions.\"\n action={\n <Button className=\"gap-2\" onClick={() => openLog()}>\n <Plus className=\"h-4 w-4\" />\n Log recovery\n </Button>\n }\n />\n ) : (\n <DataTable\n columns={columns}\n rows={logRows}\n rowKey={(r) => r.id}\n defaultSort={{ key: 'loggedAt', dir: 'desc' }}\n />\n )}\n </Section>\n </motion.div>\n\n {/* Log recovery dialog */}\n <ResponsiveDialog open={open} onOpenChange={setOpen}>\n <ResponsiveDialogContent className=\"sm:max-w-[480px]\">\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>Log recovery</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n Capture how your body is recovering — one metric per entry.\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n\n <div className=\"space-y-4 py-2\">\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-recovery-metric\">Metric</Label>\n <Select\n value={form.metric}\n onValueChange={(v) =>\n // Select only emits values from RECOVERY_METRICS, so `v` is a\n // MybRecoveryMetric — narrow the Radix `string` payload.\n setForm((f) => ({ ...f, metric: v as MybRecoveryMetric }))\n }\n >\n <SelectTrigger id=\"myb-recovery-metric\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {RECOVERY_METRICS.map((m) => (\n <SelectItem key={m} value={m}>\n {RECOVERY_META[m].label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n\n {isSleepForm && (\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-recovery-sleep\">Sleep hours</Label>\n <Input\n id=\"myb-recovery-sleep\"\n type=\"number\"\n min=\"0\"\n max=\"24\"\n step=\"0.1\"\n value={form.sleepHours}\n onChange={(e) => setForm((f) => ({ ...f, sleepHours: e.target.value }))}\n placeholder=\"e.g. 7.5\"\n />\n <p className=\"text-xs text-text-tertiary\">Primary measure for sleep entries.</p>\n </div>\n )}\n\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-recovery-score\">Score{isSleepForm ? ' (optional)' : ''}</Label>\n <Input\n id=\"myb-recovery-score\"\n type=\"number\"\n min=\"0\"\n max=\"100\"\n step=\"1\"\n value={form.score}\n onChange={(e) => setForm((f) => ({ ...f, score: e.target.value }))}\n placeholder=\"0–100\"\n />\n <p className=\"text-xs text-text-tertiary\">\n {isSleepForm\n ? 'Optional 0–100 sleep-quality score.'\n : 'A 0–100 rating for this metric.'}\n </p>\n </div>\n\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-recovery-at\">Logged at</Label>\n <Input\n id=\"myb-recovery-at\"\n type=\"datetime-local\"\n value={form.loggedAt}\n onChange={(e) => setForm((f) => ({ ...f, loggedAt: e.target.value }))}\n />\n </div>\n\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-recovery-notes\">Notes</Label>\n <Textarea\n id=\"myb-recovery-notes\"\n rows={3}\n value={form.notes}\n onChange={(e) => setForm((f) => ({ ...f, notes: e.target.value }))}\n placeholder=\"How are you feeling? (optional)\"\n />\n </div>\n\n {err && <p className=\"text-sm text-status-error-text\">{err}</p>}\n </div>\n\n <ResponsiveDialogFooter>\n <Button\n variant=\"outline\"\n onClick={() => setOpen(false)}\n disabled={logRecovery.isPending}\n >\n Cancel\n </Button>\n <Button onClick={submit} disabled={logRecovery.isPending}>\n {logRecovery.isPending ? 'Saving…' : 'Log recovery'}\n </Button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"RecoveryPage.js","names":[],"sources":["../../../../src/mybodyshield/pages/recovery/RecoveryPage.tsx"],"sourcesContent":["/**\n * Recovery — sleep, readiness, soreness, stress & mood tracking.\n * @module mybodyshield/pages/recovery\n *\n * Reads the real `mybListRecoveryLogs` array endpoint (no pagination). KPIs pull\n * the readiness / sleep / mood metrics independently so they stay stable while\n * the FilterTabs drive the charted + tabulated metric. New entries go through\n * `mybLogRecovery`. No mock data — everything is derived from the hooks.\n */\n\nimport { useMemo, useState } from 'react';\nimport { Plus, HeartPulse } from 'lucide-react';\nimport {\n ResponsiveContainer,\n AreaChart,\n Area,\n XAxis,\n YAxis,\n CartesianGrid,\n Tooltip,\n ReferenceDot,\n} from 'recharts';\nimport { motion, useReducedMotion, type MotionProps } from 'framer-motion';\nimport {\n Button,\n Input,\n Label,\n Textarea,\n Select,\n SelectTrigger,\n SelectContent,\n SelectItem,\n SelectValue,\n GlassCard,\n Spinner,\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n ResponsiveDialogDescription,\n ResponsiveDialogFooter,\n} from '@burdenoff/fe-libs/ui';\nimport {\n PageLayout,\n MetricsCard,\n FilterTabs,\n DataTable,\n LoadingView,\n ErrorView,\n Section,\n EmptyState,\n type Column,\n type FilterOption,\n} from '../../components';\nimport { useMybRecoveryLogs, useMybLogRecovery } from '../../hooks';\nimport type { MybRecoveryMetric, MybRecoveryLog, MybLogRecoveryInput } from '../../types';\nimport { formatNumber, formatDate, formatRelativeTime } from '../../utils/formatters';\nimport { RECOVERY_META, IconChip } from '../_shared/visuals';\n\ntype RecoveryFilter = 'ALL' | MybRecoveryMetric;\n\ntype Accent = 'success' | 'warning' | 'error';\n\nconst RECOVERY_METRICS: MybRecoveryMetric[] = ['SLEEP', 'READINESS', 'SORENESS', 'STRESS', 'MOOD'];\n\nconst FILTER_OPTIONS: FilterOption<RecoveryFilter>[] = [\n { value: 'ALL', label: 'All' },\n ...RECOVERY_METRICS.map((m) => ({ value: m, label: RECOVERY_META[m].label })),\n];\n\n// Brand emerald accent — dominant color for the recovery area chart & endpoint.\nconst CHART_ACCENT = 'var(--color-action-primary-bg)';\n\nconst scoreAccent = (score: number): Accent =>\n score >= 70 ? 'success' : score >= 40 ? 'warning' : 'error';\n\nconst sleepAccent = (hours: number): Accent =>\n hours >= 7 ? 'success' : hours >= 6 ? 'warning' : 'error';\n\n/** Format a Date into a `datetime-local` input value (local time, minute precision). */\nfunction toDatetimeLocalValue(date: Date): string {\n const pad = (n: number): string => String(n).padStart(2, '0');\n return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}T${pad(\n date.getHours()\n )}:${pad(date.getMinutes())}`;\n}\n\ninterface RecoveryForm {\n metric: MybRecoveryMetric;\n score: string;\n sleepHours: string;\n notes: string;\n loggedAt: string;\n}\n\nconst makeEmptyForm = (metric: MybRecoveryMetric = 'READINESS'): RecoveryForm => ({\n metric,\n score: '',\n sleepHours: '',\n notes: '',\n loggedAt: toDatetimeLocalValue(new Date()),\n});\n\nexport function RecoveryPage() {\n const [filter, setFilter] = useState<RecoveryFilter>('ALL');\n const [open, setOpen] = useState(false);\n const [form, setForm] = useState<RecoveryForm>(makeEmptyForm);\n const [err, setErr] = useState<string | null>(null);\n\n // KPI feeds — independent of the active filter so the header stays stable.\n const readiness = useMybRecoveryLogs('READINESS');\n const sleep = useMybRecoveryLogs('SLEEP');\n const mood = useMybRecoveryLogs('MOOD');\n\n // Charted + tabulated metric (undefined → all metrics).\n const selectedMetric = filter === 'ALL' ? undefined : filter;\n const logs = useMybRecoveryLogs(selectedMetric);\n\n const logRecovery = useMybLogRecovery();\n\n const reduced = useReducedMotion();\n const rise = (delay: number, hover = true): MotionProps =>\n reduced\n ? { initial: false }\n : {\n initial: { opacity: 0, y: 14 },\n animate: { opacity: 1, y: 0 },\n transition: { duration: 0.45, delay, ease: 'easeOut' },\n ...(hover\n ? {\n whileHover: {\n y: -3,\n transition: { type: 'spring', stiffness: 300, damping: 22 },\n },\n }\n : {}),\n };\n\n const readinessRows = readiness.data?.items ?? [];\n const sleepRows = sleep.data?.items ?? [];\n const moodRows = mood.data?.items ?? [];\n const logRows = logs.data?.items ?? [];\n\n const latestReadiness = useMemo(\n () =>\n [...readinessRows].sort(\n (a, b) => new Date(b.loggedAt).getTime() - new Date(a.loggedAt).getTime()\n )[0],\n [readinessRows]\n );\n\n const latestMood = useMemo(\n () =>\n [...moodRows].sort(\n (a, b) => new Date(b.loggedAt).getTime() - new Date(a.loggedAt).getTime()\n )[0],\n [moodRows]\n );\n\n const avgSleep = useMemo(() => {\n if (sleepRows.length === 0) return null;\n const sum = sleepRows.reduce((acc, l) => acc + l.sleepHours, 0);\n return sum / sleepRows.length;\n }, [sleepRows]);\n\n const isSleepSelected = filter === 'SLEEP';\n const selectedLabel = filter === 'ALL' ? 'All metrics' : RECOVERY_META[filter].label;\n\n const chartData = useMemo(\n () =>\n [...logRows]\n .sort((a, b) => new Date(a.loggedAt).getTime() - new Date(b.loggedAt).getTime())\n .map((l) => ({\n label: formatDate(l.loggedAt),\n value: isSleepSelected ? l.sleepHours : l.score,\n })),\n [logRows, isSleepSelected]\n );\n const lastPoint = chartData.length > 0 ? chartData[chartData.length - 1] : undefined;\n\n const openLog = (metric: MybRecoveryMetric = 'READINESS') => {\n setForm(makeEmptyForm(metric));\n setErr(null);\n setOpen(true);\n };\n\n const isSleepForm = form.metric === 'SLEEP';\n\n const submit = async () => {\n setErr(null);\n if (isSleepForm) {\n if (form.sleepHours.trim() === '') {\n setErr('Sleep hours is required for sleep entries.');\n return;\n }\n const h = Number(form.sleepHours);\n if (!Number.isFinite(h) || h < 0 || h > 24) {\n setErr('Enter sleep hours between 0 and 24.');\n return;\n }\n } else {\n if (form.score.trim() === '') {\n setErr('A score is required.');\n return;\n }\n }\n if (form.score.trim() !== '') {\n const s = Number(form.score);\n if (!Number.isFinite(s) || s < 0 || s > 100) {\n setErr('Enter a score between 0 and 100.');\n return;\n }\n }\n\n const input: MybLogRecoveryInput = {\n metric: form.metric,\n score: form.score.trim() !== '' ? Number(form.score) : undefined,\n sleepHours: form.sleepHours.trim() !== '' ? Number(form.sleepHours) : undefined,\n notes: form.notes.trim() || undefined,\n loggedAt: form.loggedAt ? new Date(form.loggedAt).toISOString() : undefined,\n };\n\n try {\n await logRecovery.mutateAsync(input);\n setOpen(false);\n } catch (e) {\n setErr(e instanceof Error ? e.message : 'Failed to log recovery.');\n }\n };\n\n const columns: Column<MybRecoveryLog>[] = [\n {\n key: 'metric',\n header: 'Metric',\n sortValue: (r) => RECOVERY_META[r.metric].label,\n render: (r) => {\n const meta = RECOVERY_META[r.metric];\n const Icon = meta.icon;\n return (\n <span className=\"inline-flex items-center gap-2.5\">\n <IconChip icon={<Icon className=\"h-4 w-4\" />} tint={meta.tint} />\n <span className=\"font-medium text-text-primary\">{meta.label}</span>\n </span>\n );\n },\n },\n {\n key: 'value',\n header: 'Value',\n align: 'right',\n sortValue: (r) => (r.metric === 'SLEEP' ? r.sleepHours : r.score),\n render: (r) =>\n r.metric === 'SLEEP' ? (\n <span className=\"tabular-nums text-text-primary\">\n {formatNumber(r.sleepHours, { maximumFractionDigits: 1 })} h\n </span>\n ) : (\n <span className=\"tabular-nums text-text-primary\">{formatNumber(r.score)}</span>\n ),\n },\n {\n key: 'notes',\n header: 'Notes',\n hideOnMobile: true,\n render: (r) =>\n r.notes && r.notes.trim() ? (\n <span className=\"text-text-secondary\">{r.notes}</span>\n ) : (\n <span className=\"text-text-tertiary\">—</span>\n ),\n },\n {\n key: 'loggedAt',\n header: 'Logged',\n align: 'right',\n sortValue: (r) => new Date(r.loggedAt).getTime(),\n render: (r) => (\n <span className=\"whitespace-nowrap text-text-tertiary\">\n {formatRelativeTime(r.loggedAt)}\n </span>\n ),\n },\n ];\n\n const ReadinessIcon = RECOVERY_META.READINESS.icon;\n const SleepIcon = RECOVERY_META.SLEEP.icon;\n const MoodIcon = RECOVERY_META.MOOD.icon;\n\n return (\n <PageLayout\n title=\"Recovery\"\n description=\"Sleep, readiness, soreness, stress & mood.\"\n actions={\n <Button className=\"gap-2\" onClick={() => openLog()}>\n <Plus className=\"h-4 w-4\" />\n Log recovery\n </Button>\n }\n >\n {/* KPI grid */}\n <motion.div {...rise(0)} className=\"grid grid-cols-1 gap-4 sm:grid-cols-3\">\n <MetricsCard\n icon={\n <IconChip\n icon={<ReadinessIcon className=\"h-5 w-5\" />}\n tint={RECOVERY_META.READINESS.tint}\n />\n }\n value={latestReadiness ? formatNumber(latestReadiness.score) : '—'}\n label=\"Latest readiness\"\n accent={latestReadiness ? scoreAccent(latestReadiness.score) : undefined}\n footer={\n latestReadiness ? (\n <p className=\"text-xs text-text-tertiary\">\n {formatRelativeTime(latestReadiness.loggedAt)}\n </p>\n ) : (\n <button\n type=\"button\"\n onClick={() => openLog('READINESS')}\n className=\"text-xs font-medium text-accent hover:underline\"\n >\n Log readiness →\n </button>\n )\n }\n />\n <MetricsCard\n icon={\n <IconChip icon={<SleepIcon className=\"h-5 w-5\" />} tint={RECOVERY_META.SLEEP.tint} />\n }\n value={\n avgSleep != null ? `${formatNumber(avgSleep, { maximumFractionDigits: 1 })} h` : '—'\n }\n label=\"Avg sleep\"\n accent={avgSleep != null ? sleepAccent(avgSleep) : undefined}\n footer={\n sleepRows.length > 0 ? (\n <p className=\"text-xs text-text-tertiary\">\n <span className=\"tabular-nums text-text-secondary\">{sleepRows.length}</span>{' '}\n {sleepRows.length === 1 ? 'night' : 'nights'} logged\n </p>\n ) : (\n <button\n type=\"button\"\n onClick={() => openLog('SLEEP')}\n className=\"text-xs font-medium text-accent hover:underline\"\n >\n Log sleep →\n </button>\n )\n }\n />\n <MetricsCard\n icon={<IconChip icon={<MoodIcon className=\"h-5 w-5\" />} tint={RECOVERY_META.MOOD.tint} />}\n value={latestMood ? formatNumber(latestMood.score) : '—'}\n label=\"Latest mood\"\n accent={latestMood ? scoreAccent(latestMood.score) : undefined}\n footer={\n latestMood ? (\n <p className=\"text-xs text-text-tertiary\">\n {formatRelativeTime(latestMood.loggedAt)}\n </p>\n ) : (\n <button\n type=\"button\"\n onClick={() => openLog('MOOD')}\n className=\"text-xs font-medium text-accent hover:underline\"\n >\n Log mood →\n </button>\n )\n }\n />\n </motion.div>\n\n {/* Metric filter */}\n <div className=\"mt-6 flex flex-wrap items-center gap-3\">\n <FilterTabs options={FILTER_OPTIONS} value={filter} onChange={setFilter} />\n </div>\n\n {/* Trend chart */}\n <Section\n title={`${selectedLabel} trend`}\n description={isSleepSelected ? 'Sleep hours over time.' : 'Recovery score over time.'}\n >\n <motion.div {...rise(0.08)}>\n <GlassCard\n treatment=\"glass\"\n className=\"rounded-2xl p-6 transition-shadow duration-300 hover:shadow-2xl\"\n >\n {logs.isLoading ? (\n <div className=\"flex h-[280px] items-center justify-center\">\n <Spinner size=\"lg\" />\n </div>\n ) : logs.isError ? (\n <ErrorView\n message=\"We couldn't load this metric's history.\"\n onRetry={() => logs.refetch()}\n />\n ) : chartData.length === 0 ? (\n <div className=\"flex h-[280px] flex-col items-center justify-center text-center\">\n <span className=\"flex size-11 items-center justify-center rounded-full bg-[var(--color-accent-soft)]\">\n <HeartPulse className=\"h-5 w-5 text-accent\" />\n </span>\n <p className=\"mt-3 text-sm font-medium text-text-primary\">Nothing to chart yet</p>\n <p className=\"mt-1 max-w-xs text-xs text-text-tertiary\">\n Log a few {selectedLabel.toLowerCase()} entries to see your trend here.\n </p>\n </div>\n ) : (\n <ResponsiveContainer width=\"100%\" height={280}>\n <AreaChart data={chartData} margin={{ top: 12, right: 16, bottom: 4, left: 4 }}>\n <defs>\n <linearGradient id=\"myb-recovery-fill\" x1=\"0\" y1=\"0\" x2=\"0\" y2=\"1\">\n <stop offset=\"0%\" stopColor={CHART_ACCENT} stopOpacity={0.38} />\n <stop offset=\"55%\" stopColor={CHART_ACCENT} stopOpacity={0.12} />\n <stop offset=\"100%\" stopColor={CHART_ACCENT} stopOpacity={0} />\n </linearGradient>\n </defs>\n <CartesianGrid\n vertical={false}\n stroke=\"var(--color-border-seam)\"\n strokeDasharray=\"4 4\"\n />\n <XAxis\n dataKey=\"label\"\n tickLine={false}\n axisLine={false}\n tickMargin={8}\n minTickGap={20}\n tick={{ fill: 'var(--color-text-tertiary)', fontSize: 12 }}\n />\n <YAxis\n width={44}\n tickLine={false}\n axisLine={false}\n tick={{ fill: 'var(--color-text-tertiary)', fontSize: 12 }}\n />\n <Tooltip\n contentStyle={{\n background: 'var(--color-bg-surface)',\n border: '1px solid var(--color-border-seam)',\n borderRadius: 12,\n color: 'var(--color-text-primary)',\n fontSize: 12,\n }}\n labelStyle={{ color: 'var(--color-text-tertiary)' }}\n cursor={{ stroke: 'var(--color-border-seam)', strokeDasharray: '4 4' }}\n />\n <Area\n type=\"monotone\"\n dataKey=\"value\"\n name={isSleepSelected ? 'Sleep hours' : 'Score'}\n stroke={CHART_ACCENT}\n strokeWidth={2.5}\n fill=\"url(#myb-recovery-fill)\"\n fillOpacity={1}\n dot={false}\n activeDot={{\n r: 4,\n strokeWidth: 2,\n stroke: 'var(--color-bg-surface)',\n fill: CHART_ACCENT,\n }}\n isAnimationActive={!reduced}\n animationDuration={900}\n />\n {lastPoint && (\n <ReferenceDot\n x={lastPoint.label}\n y={lastPoint.value}\n r={12}\n isFront\n ifOverflow=\"visible\"\n fill={CHART_ACCENT}\n fillOpacity={0.16}\n stroke=\"none\"\n />\n )}\n {lastPoint && (\n <ReferenceDot\n x={lastPoint.label}\n y={lastPoint.value}\n r={5}\n isFront\n ifOverflow=\"visible\"\n fill={CHART_ACCENT}\n stroke=\"var(--color-bg-surface)\"\n strokeWidth={2}\n />\n )}\n </AreaChart>\n </ResponsiveContainer>\n )}\n </GlassCard>\n </motion.div>\n </Section>\n\n {/* Log table */}\n <motion.div {...rise(0.16, false)}>\n <Section title=\"Recovery log\" description=\"Every recovery entry, most recent first.\">\n {logs.isLoading ? (\n <LoadingView label=\"Loading recovery logs…\" />\n ) : logs.isError ? (\n <ErrorView\n message=\"We couldn't load your recovery logs.\"\n onRetry={() => logs.refetch()}\n />\n ) : logRows.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n icon={<HeartPulse className=\"h-10 w-10\" />}\n title=\"No recovery logs yet\"\n description=\"Track your sleep, readiness, soreness, stress or mood to see how your body is recovering between sessions.\"\n action={\n <Button className=\"gap-2\" onClick={() => openLog()}>\n <Plus className=\"h-4 w-4\" />\n Log recovery\n </Button>\n }\n />\n ) : (\n <DataTable\n columns={columns}\n rows={logRows}\n rowKey={(r) => r.id}\n defaultSort={{ key: 'loggedAt', dir: 'desc' }}\n />\n )}\n </Section>\n </motion.div>\n\n {/* Log recovery dialog */}\n <ResponsiveDialog open={open} onOpenChange={setOpen}>\n <ResponsiveDialogContent className=\"sm:max-w-[480px]\">\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>Log recovery</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n Capture how your body is recovering — one metric per entry.\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n\n <div className=\"space-y-4 py-2\">\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-recovery-metric\">Metric</Label>\n <Select\n value={form.metric}\n onValueChange={(v) =>\n // Select only emits values from RECOVERY_METRICS, so `v` is a\n // MybRecoveryMetric — narrow the Radix `string` payload.\n setForm((f) => ({ ...f, metric: v as MybRecoveryMetric }))\n }\n >\n <SelectTrigger id=\"myb-recovery-metric\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {RECOVERY_METRICS.map((m) => (\n <SelectItem key={m} value={m}>\n {RECOVERY_META[m].label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n\n {isSleepForm && (\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-recovery-sleep\">Sleep hours</Label>\n <Input\n id=\"myb-recovery-sleep\"\n type=\"number\"\n min=\"0\"\n max=\"24\"\n step=\"0.1\"\n value={form.sleepHours}\n onChange={(e) => setForm((f) => ({ ...f, sleepHours: e.target.value }))}\n placeholder=\"e.g. 7.5\"\n />\n <p className=\"text-xs text-text-tertiary\">Primary measure for sleep entries.</p>\n </div>\n )}\n\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-recovery-score\">Score{isSleepForm ? ' (optional)' : ''}</Label>\n <Input\n id=\"myb-recovery-score\"\n type=\"number\"\n min=\"0\"\n max=\"100\"\n step=\"1\"\n value={form.score}\n onChange={(e) => setForm((f) => ({ ...f, score: e.target.value }))}\n placeholder=\"0–100\"\n />\n <p className=\"text-xs text-text-tertiary\">\n {isSleepForm\n ? 'Optional 0–100 sleep-quality score.'\n : 'A 0–100 rating for this metric.'}\n </p>\n </div>\n\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-recovery-at\">Logged at</Label>\n <Input\n id=\"myb-recovery-at\"\n type=\"datetime-local\"\n value={form.loggedAt}\n onChange={(e) => setForm((f) => ({ ...f, loggedAt: e.target.value }))}\n />\n </div>\n\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-recovery-notes\">Notes</Label>\n <Textarea\n id=\"myb-recovery-notes\"\n rows={3}\n value={form.notes}\n onChange={(e) => setForm((f) => ({ ...f, notes: e.target.value }))}\n placeholder=\"How are you feeling? (optional)\"\n />\n </div>\n\n {err && <p className=\"text-sm text-status-error-text\">{err}</p>}\n </div>\n\n <ResponsiveDialogFooter>\n <Button\n variant=\"outline\"\n onClick={() => setOpen(false)}\n disabled={logRecovery.isPending}\n >\n Cancel\n </Button>\n <Button onClick={submit} disabled={logRecovery.isPending}>\n {logRecovery.isPending ? 'Saving…' : 'Log recovery'}\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.716.1",
3
+ "version": "2026.720.1",
4
4
  "description": "Burdenoff MyBodyShield Microfrontend — health & fitness (training, nutrition, progress, wellbeing)",
5
5
  "type": "module",
6
6
  "files": [