@burdenoff/microfe-mybodyshield 2026.1026.1 → 2026.1026.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/mybodyshield/MyBodyShieldRoutes.d.ts.map +1 -1
- package/dist/mybodyshield/MyBodyShieldRoutes.js +30 -23
- package/dist/mybodyshield/MyBodyShieldRoutes.js.map +1 -1
- package/dist/mybodyshield/hooks/useMyBodyShieldApi.d.ts +2 -0
- package/dist/mybodyshield/hooks/useMyBodyShieldApi.d.ts.map +1 -1
- package/dist/mybodyshield/hooks/useMyBodyShieldApi.js +5 -4
- package/dist/mybodyshield/hooks/useMyBodyShieldApi.js.map +1 -1
- package/dist/mybodyshield/pages/coaching/CoachClientPage.d.ts +2 -0
- package/dist/mybodyshield/pages/coaching/CoachClientPage.d.ts.map +1 -0
- package/dist/mybodyshield/pages/coaching/CoachClientPage.js +264 -0
- package/dist/mybodyshield/pages/coaching/CoachClientPage.js.map +1 -0
- package/dist/mybodyshield/pages/coaching/CoachingPage.d.ts.map +1 -1
- package/dist/mybodyshield/pages/coaching/CoachingPage.js +74 -67
- package/dist/mybodyshield/pages/coaching/CoachingPage.js.map +1 -1
- package/dist/mybodyshield/pages/coaching/coachAnalytics.d.ts +6 -0
- package/dist/mybodyshield/pages/coaching/coachAnalytics.d.ts.map +1 -0
- package/dist/mybodyshield/pages/coaching/coachAnalytics.js +13 -0
- package/dist/mybodyshield/pages/coaching/coachAnalytics.js.map +1 -0
- package/package.json +2 -1
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
import { useMyBodyShield as e } from "../../context/MyBodyShieldContext.js";
|
|
2
|
+
import { PageLayout as t } from "../../components/PageLayout.js";
|
|
3
|
+
import { MetricsCard as n } from "../../components/MetricsCard.js";
|
|
4
|
+
import { EmptyState as r } from "../../components/EmptyState.js";
|
|
5
|
+
import "../../context/index.js";
|
|
6
|
+
import { useMybAddWorkout as i, useMybCoaching as ee, useMybCreateNutritionPlan as te, useMybCreateProgram as a, useMybInsights as o } from "../../hooks/useMyBodyShieldApi.js";
|
|
7
|
+
import { useMyBodyShieldAnalytics as s } from "../../hooks/useMyBodyShieldAnalytics.js";
|
|
8
|
+
import { useTr as c } from "../../hooks/useTr.js";
|
|
9
|
+
import "../../hooks/index.js";
|
|
10
|
+
import { ErrorView as l, LoadingView as u, Section as d } from "../../components/StateViews.js";
|
|
11
|
+
import "../../components/index.js";
|
|
12
|
+
import { formatCoachPercent as ne, hasCoachAnalyticsData as re } from "./coachAnalytics.js";
|
|
13
|
+
import { useEffect as ie, useMemo as ae, useState as f } from "react";
|
|
14
|
+
import { Activity as p, ArrowLeft as m, Dumbbell as h, HeartPulse as g, Plus as oe, Salad as _, Target as se } from "lucide-react";
|
|
15
|
+
import { useParams as v } from "react-router";
|
|
16
|
+
import { jsx as y, jsxs as b } from "react/jsx-runtime";
|
|
17
|
+
import { Button as x, GlassCard as S, Input as C, Label as w } from "@burdenoff/fe-libs/ui";
|
|
18
|
+
//#region src/mybodyshield/pages/coaching/CoachClientPage.tsx
|
|
19
|
+
var T = 30;
|
|
20
|
+
function E() {
|
|
21
|
+
let e = /* @__PURE__ */ new Date(), t = new Date(e);
|
|
22
|
+
return t.setDate(e.getDate() - T + 1), {
|
|
23
|
+
dateRangeStart: t.toISOString(),
|
|
24
|
+
dateRangeEnd: e.toISOString()
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
function D() {
|
|
28
|
+
let T = c(), { memberId: D } = v(), { currentUser: O, workspaceId: k, navigateTo: A } = e(), j = s(), ce = ae(() => E(), []), M = ee("ACTIVE", D), N = M.data?.items[0], P = o({
|
|
29
|
+
...ce,
|
|
30
|
+
memberId: D
|
|
31
|
+
}), F = a(), I = i(), L = te(), [R, z] = f(""), [B, V] = f(""), [H, U] = f(""), [W, G] = f(""), [K, q] = f(""), [J, Y] = f(null), [X, Z] = f(null), le = N?.coachId === O?.id, Q = re(P.data), $ = (P.data?.daily ?? []).filter((e) => e.sessions > 0 || e.weight != null || e.readiness != null || e.sleepHours != null).slice(-7).reverse(), ue = T("mybodyshield.pages.coaching.clientTitle", "Client workspace");
|
|
32
|
+
ie(() => {
|
|
33
|
+
N?.id && D && j.emitPageView("coaching_client", {
|
|
34
|
+
workspaceId: k,
|
|
35
|
+
entityId: N.id,
|
|
36
|
+
memberId: D
|
|
37
|
+
});
|
|
38
|
+
}, [
|
|
39
|
+
j,
|
|
40
|
+
D,
|
|
41
|
+
N?.id,
|
|
42
|
+
k
|
|
43
|
+
]);
|
|
44
|
+
let de = async (e) => {
|
|
45
|
+
if (e.preventDefault(), !D || !R.trim() || !B.trim()) {
|
|
46
|
+
Y(T("mybodyshield.pages.coaching.planRequired", "Program and first workout names are required."));
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
Y(null), Z(null);
|
|
50
|
+
try {
|
|
51
|
+
let e = await F.mutateAsync({
|
|
52
|
+
name: R.trim(),
|
|
53
|
+
memberId: D,
|
|
54
|
+
coachId: O?.id,
|
|
55
|
+
weeks: 4,
|
|
56
|
+
daysPerWeek: 3
|
|
57
|
+
});
|
|
58
|
+
await I.mutateAsync({
|
|
59
|
+
programId: e.id,
|
|
60
|
+
name: B.trim(),
|
|
61
|
+
dayOfWeek: 1,
|
|
62
|
+
weekNumber: 1,
|
|
63
|
+
durationMin: 45
|
|
64
|
+
}), H.trim() && await L.mutateAsync({
|
|
65
|
+
name: H.trim(),
|
|
66
|
+
memberId: D,
|
|
67
|
+
coachId: O?.id,
|
|
68
|
+
goalType: "GENERAL_FITNESS",
|
|
69
|
+
calorieTarget: Number(W) || void 0,
|
|
70
|
+
proteinTarget: Number(K) || void 0
|
|
71
|
+
}), Z(T("mybodyshield.pages.coaching.planSaved", "Plan assigned to client.")), z(""), V(""), U(""), G(""), q(""), j.emitFact("coaching", "plan_assigned", {
|
|
72
|
+
workspaceId: k,
|
|
73
|
+
memberId: D
|
|
74
|
+
});
|
|
75
|
+
} catch (e) {
|
|
76
|
+
Y(e instanceof Error ? e.message : T("mybodyshield.common.unknownError", "Unknown error"));
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
return M.isLoading || P.isLoading ? /* @__PURE__ */ y(u, { label: T("mybodyshield.pages.coaching.clientLoading", "Loading client workspace…") }) : M.isError || P.isError ? /* @__PURE__ */ y(l, {
|
|
80
|
+
message: T("mybodyshield.pages.coaching.clientError", "Client data could not be loaded."),
|
|
81
|
+
onRetry: () => {
|
|
82
|
+
M.refetch(), P.refetch();
|
|
83
|
+
}
|
|
84
|
+
}) : !D || !N || !le ? /* @__PURE__ */ y(r, {
|
|
85
|
+
illustration: "empty-people",
|
|
86
|
+
title: T("mybodyshield.pages.coaching.clientUnavailable", "Client workspace unavailable"),
|
|
87
|
+
description: T("mybodyshield.pages.coaching.clientUnavailableDescription", "Only an active coaching relationship with the required consent can open this workspace."),
|
|
88
|
+
action: /* @__PURE__ */ b(x, {
|
|
89
|
+
onClick: () => A("coaching"),
|
|
90
|
+
children: [/* @__PURE__ */ y(m, { className: "mr-2 h-4 w-4" }), T("mybodyshield.common.back", "Back")]
|
|
91
|
+
})
|
|
92
|
+
}) : /* @__PURE__ */ b(t, {
|
|
93
|
+
title: ue,
|
|
94
|
+
description: T("mybodyshield.pages.coaching.clientDescription", "Consent-scoped progress and plan tools for client #{{id}}.", { id: D.slice(0, 8) }),
|
|
95
|
+
actions: /* @__PURE__ */ b(x, {
|
|
96
|
+
variant: "outline",
|
|
97
|
+
onClick: () => A("coaching"),
|
|
98
|
+
children: [/* @__PURE__ */ y(m, { className: "mr-2 h-4 w-4" }), T("mybodyshield.common.back", "Coaching")]
|
|
99
|
+
}),
|
|
100
|
+
children: [
|
|
101
|
+
Q ? /* @__PURE__ */ y(d, {
|
|
102
|
+
title: T("mybodyshield.pages.coaching.analyticsTitle", "Last 30 days"),
|
|
103
|
+
description: T("mybodyshield.pages.coaching.analyticsDescription", "Values come from the client’s consented training, nutrition, and progress records."),
|
|
104
|
+
children: /* @__PURE__ */ b("div", {
|
|
105
|
+
className: "grid grid-cols-1 gap-4 sm:grid-cols-2 xl:grid-cols-4",
|
|
106
|
+
children: [
|
|
107
|
+
/* @__PURE__ */ y(n, {
|
|
108
|
+
icon: /* @__PURE__ */ y(h, { className: "h-5 w-5" }),
|
|
109
|
+
value: P.data?.summary.totalSessions ?? 0,
|
|
110
|
+
label: T("mybodyshield.pages.coaching.completedWorkouts", "Completed workouts")
|
|
111
|
+
}),
|
|
112
|
+
/* @__PURE__ */ y(n, {
|
|
113
|
+
icon: /* @__PURE__ */ y(_, { className: "h-5 w-5" }),
|
|
114
|
+
value: ne(P.data?.summary.macroAdherencePercent ?? 0),
|
|
115
|
+
label: T("mybodyshield.pages.coaching.nutritionAdherence", "Nutrition adherence")
|
|
116
|
+
}),
|
|
117
|
+
/* @__PURE__ */ y(n, {
|
|
118
|
+
icon: /* @__PURE__ */ y(g, { className: "h-5 w-5" }),
|
|
119
|
+
value: Math.round(P.data?.summary.averageReadiness ?? 0) || "—",
|
|
120
|
+
label: T("mybodyshield.pages.coaching.averageReadiness", "Average readiness")
|
|
121
|
+
}),
|
|
122
|
+
/* @__PURE__ */ y(n, {
|
|
123
|
+
icon: /* @__PURE__ */ y(p, { className: "h-5 w-5" }),
|
|
124
|
+
value: P.data?.summary.consistencyStreakDays ?? 0,
|
|
125
|
+
label: T("mybodyshield.pages.coaching.consistencyStreak", "Day streak")
|
|
126
|
+
})
|
|
127
|
+
]
|
|
128
|
+
})
|
|
129
|
+
}) : /* @__PURE__ */ y(r, {
|
|
130
|
+
illustration: "empty-data",
|
|
131
|
+
title: T("mybodyshield.pages.coaching.noAnalytics", "No client analytics yet"),
|
|
132
|
+
description: T("mybodyshield.pages.coaching.noAnalyticsDescription", "Analytics appear after the client logs workouts, meals, recovery, or body metrics.")
|
|
133
|
+
}),
|
|
134
|
+
Q && /* @__PURE__ */ y(d, {
|
|
135
|
+
title: T("mybodyshield.pages.coaching.recentActivity", "Recent activity"),
|
|
136
|
+
description: T("mybodyshield.pages.coaching.recentActivityDescription", "Only days with recorded client data are shown."),
|
|
137
|
+
children: $.length === 0 ? /* @__PURE__ */ y(r, { title: T("mybodyshield.pages.coaching.noRecentActivity", "No recent activity") }) : /* @__PURE__ */ y("div", {
|
|
138
|
+
className: "grid gap-3 sm:grid-cols-2 lg:grid-cols-3",
|
|
139
|
+
children: $.map((e) => /* @__PURE__ */ b(S, {
|
|
140
|
+
treatment: "plain",
|
|
141
|
+
className: "p-4",
|
|
142
|
+
children: [/* @__PURE__ */ y("p", {
|
|
143
|
+
className: "text-xs font-medium uppercase tracking-wide text-text-tertiary",
|
|
144
|
+
children: new Date(e.date).toLocaleDateString()
|
|
145
|
+
}), /* @__PURE__ */ b("div", {
|
|
146
|
+
className: "mt-2 flex flex-wrap gap-x-4 gap-y-1 text-sm text-text-secondary",
|
|
147
|
+
children: [
|
|
148
|
+
e.sessions > 0 && /* @__PURE__ */ b("span", { children: [
|
|
149
|
+
e.sessions,
|
|
150
|
+
" workout",
|
|
151
|
+
e.sessions === 1 ? "" : "s"
|
|
152
|
+
] }),
|
|
153
|
+
e.weight != null && /* @__PURE__ */ b("span", { children: [e.weight, " weight"] }),
|
|
154
|
+
e.readiness != null && /* @__PURE__ */ b("span", { children: [e.readiness, " readiness"] }),
|
|
155
|
+
e.sleepHours != null && /* @__PURE__ */ b("span", { children: [e.sleepHours, "h sleep"] })
|
|
156
|
+
]
|
|
157
|
+
})]
|
|
158
|
+
}, e.date))
|
|
159
|
+
})
|
|
160
|
+
}),
|
|
161
|
+
/* @__PURE__ */ y(d, {
|
|
162
|
+
title: T("mybodyshield.pages.coaching.planBuilderTitle", "Build a client plan"),
|
|
163
|
+
description: T("mybodyshield.pages.coaching.planBuilderDescription", "Creates one real training program with its first workout. Nutrition targets are optional and use the existing plan model."),
|
|
164
|
+
children: /* @__PURE__ */ y(S, {
|
|
165
|
+
treatment: "plain",
|
|
166
|
+
className: "max-w-3xl p-5",
|
|
167
|
+
children: /* @__PURE__ */ b("form", {
|
|
168
|
+
className: "grid gap-4 sm:grid-cols-2",
|
|
169
|
+
onSubmit: (e) => void de(e),
|
|
170
|
+
children: [
|
|
171
|
+
/* @__PURE__ */ b("div", {
|
|
172
|
+
className: "space-y-2",
|
|
173
|
+
children: [/* @__PURE__ */ y(w, {
|
|
174
|
+
htmlFor: "coach-program-name",
|
|
175
|
+
children: T("mybodyshield.pages.coaching.programName", "Program name")
|
|
176
|
+
}), /* @__PURE__ */ y(C, {
|
|
177
|
+
id: "coach-program-name",
|
|
178
|
+
value: R,
|
|
179
|
+
onChange: (e) => z(e.target.value)
|
|
180
|
+
})]
|
|
181
|
+
}),
|
|
182
|
+
/* @__PURE__ */ b("div", {
|
|
183
|
+
className: "space-y-2",
|
|
184
|
+
children: [/* @__PURE__ */ y(w, {
|
|
185
|
+
htmlFor: "coach-workout-name",
|
|
186
|
+
children: T("mybodyshield.pages.coaching.firstWorkout", "First workout")
|
|
187
|
+
}), /* @__PURE__ */ y(C, {
|
|
188
|
+
id: "coach-workout-name",
|
|
189
|
+
value: B,
|
|
190
|
+
onChange: (e) => V(e.target.value)
|
|
191
|
+
})]
|
|
192
|
+
}),
|
|
193
|
+
/* @__PURE__ */ b("div", {
|
|
194
|
+
className: "space-y-2",
|
|
195
|
+
children: [/* @__PURE__ */ y(w, {
|
|
196
|
+
htmlFor: "coach-nutrition-name",
|
|
197
|
+
children: T("mybodyshield.pages.coaching.nutritionPlanName", "Nutrition plan (optional)")
|
|
198
|
+
}), /* @__PURE__ */ y(C, {
|
|
199
|
+
id: "coach-nutrition-name",
|
|
200
|
+
value: H,
|
|
201
|
+
onChange: (e) => U(e.target.value)
|
|
202
|
+
})]
|
|
203
|
+
}),
|
|
204
|
+
/* @__PURE__ */ b("div", {
|
|
205
|
+
className: "grid grid-cols-2 gap-3",
|
|
206
|
+
children: [/* @__PURE__ */ b("div", {
|
|
207
|
+
className: "space-y-2",
|
|
208
|
+
children: [/* @__PURE__ */ y(w, {
|
|
209
|
+
htmlFor: "coach-calories",
|
|
210
|
+
children: T("mybodyshield.pages.coaching.calorieTarget", "Calories")
|
|
211
|
+
}), /* @__PURE__ */ y(C, {
|
|
212
|
+
id: "coach-calories",
|
|
213
|
+
inputMode: "numeric",
|
|
214
|
+
value: W,
|
|
215
|
+
onChange: (e) => G(e.target.value)
|
|
216
|
+
})]
|
|
217
|
+
}), /* @__PURE__ */ b("div", {
|
|
218
|
+
className: "space-y-2",
|
|
219
|
+
children: [/* @__PURE__ */ y(w, {
|
|
220
|
+
htmlFor: "coach-protein",
|
|
221
|
+
children: T("mybodyshield.pages.coaching.proteinTarget", "Protein g")
|
|
222
|
+
}), /* @__PURE__ */ y(C, {
|
|
223
|
+
id: "coach-protein",
|
|
224
|
+
inputMode: "numeric",
|
|
225
|
+
value: K,
|
|
226
|
+
onChange: (e) => q(e.target.value)
|
|
227
|
+
})]
|
|
228
|
+
})]
|
|
229
|
+
}),
|
|
230
|
+
J && /* @__PURE__ */ y("p", {
|
|
231
|
+
className: "sm:col-span-2 rounded-lg bg-status-error-bg-subtle px-3 py-2 text-sm text-status-error-text",
|
|
232
|
+
children: J
|
|
233
|
+
}),
|
|
234
|
+
X && /* @__PURE__ */ y("p", {
|
|
235
|
+
className: "sm:col-span-2 rounded-lg bg-status-success-bg-subtle px-3 py-2 text-sm text-status-success-text",
|
|
236
|
+
children: X
|
|
237
|
+
}),
|
|
238
|
+
/* @__PURE__ */ y("div", {
|
|
239
|
+
className: "sm:col-span-2",
|
|
240
|
+
children: /* @__PURE__ */ b(x, {
|
|
241
|
+
type: "submit",
|
|
242
|
+
disabled: F.isPending || I.isPending || L.isPending,
|
|
243
|
+
children: [/* @__PURE__ */ y(oe, { className: "mr-2 h-4 w-4" }), T("mybodyshield.pages.coaching.assignPlan", "Assign plan")]
|
|
244
|
+
})
|
|
245
|
+
})
|
|
246
|
+
]
|
|
247
|
+
})
|
|
248
|
+
})
|
|
249
|
+
}),
|
|
250
|
+
/* @__PURE__ */ y(d, {
|
|
251
|
+
title: T("mybodyshield.pages.coaching.scopeTitle", "Consent scope"),
|
|
252
|
+
children: /* @__PURE__ */ b(S, {
|
|
253
|
+
treatment: "plain",
|
|
254
|
+
className: "flex items-center gap-3 p-5 text-sm text-text-secondary",
|
|
255
|
+
children: [/* @__PURE__ */ y(se, { className: "h-5 w-5 shrink-0 text-status-success-text" }), T("mybodyshield.pages.coaching.scopeDescription", "This view is available only while the active relationship shares workouts, nutrition, and metrics.")]
|
|
256
|
+
})
|
|
257
|
+
})
|
|
258
|
+
]
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
//#endregion
|
|
262
|
+
export { D as CoachClientPage };
|
|
263
|
+
|
|
264
|
+
//# sourceMappingURL=CoachClientPage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CoachClientPage.js","names":[],"sources":["../../../../src/mybodyshield/pages/coaching/CoachClientPage.tsx"],"sourcesContent":["import { useEffect, useMemo, useState, type FormEvent } from 'react';\nimport { useParams } from 'react-router';\nimport { Activity, ArrowLeft, Dumbbell, HeartPulse, Plus, Salad, Target } from 'lucide-react';\nimport { Button, GlassCard, Input, Label } from '@burdenoff/fe-libs/ui';\nimport {\n EmptyState,\n ErrorView,\n LoadingView,\n MetricsCard,\n PageLayout,\n Section,\n} from '../../components';\nimport { useMyBodyShield } from '../../context';\nimport {\n useMybAddWorkout,\n useMybCoaching,\n useMybCreateNutritionPlan,\n useMybCreateProgram,\n useMybInsights,\n useMyBodyShieldAnalytics,\n useTr,\n} from '../../hooks';\nimport type { MybGoalType } from '../../types';\nimport { formatCoachPercent, hasCoachAnalyticsData } from './coachAnalytics';\n\nconst RANGE_DAYS = 30;\n\nfunction dateRange() {\n const end = new Date();\n const start = new Date(end);\n start.setDate(end.getDate() - RANGE_DAYS + 1);\n return { dateRangeStart: start.toISOString(), dateRangeEnd: end.toISOString() };\n}\n\nexport function CoachClientPage() {\n const t = useTr();\n const { memberId } = useParams<{ memberId: string }>();\n const { currentUser, workspaceId, navigateTo } = useMyBodyShield();\n const analytics = useMyBodyShieldAnalytics();\n const range = useMemo(() => dateRange(), []);\n const relationshipQuery = useMybCoaching('ACTIVE', memberId);\n const relationship = relationshipQuery.data?.items[0];\n const insights = useMybInsights({ ...range, memberId });\n const createProgram = useMybCreateProgram();\n const addWorkout = useMybAddWorkout();\n const createNutritionPlan = useMybCreateNutritionPlan();\n const [programName, setProgramName] = useState('');\n const [workoutName, setWorkoutName] = useState('');\n const [nutritionName, setNutritionName] = useState('');\n const [calories, setCalories] = useState('');\n const [protein, setProtein] = useState('');\n const [error, setError] = useState<string | null>(null);\n const [saved, setSaved] = useState<string | null>(null);\n\n const isCoach = relationship?.coachId === currentUser?.id;\n const hasData = hasCoachAnalyticsData(insights.data);\n const recentActivity = (insights.data?.daily ?? [])\n .filter(\n (day) =>\n day.sessions > 0 || day.weight != null || day.readiness != null || day.sleepHours != null\n )\n .slice(-7)\n .reverse();\n const pageTitle = t('mybodyshield.pages.coaching.clientTitle', 'Client workspace');\n\n useEffect(() => {\n if (relationship?.id && memberId) {\n analytics.emitPageView('coaching_client', {\n workspaceId,\n entityId: relationship.id,\n memberId,\n });\n }\n }, [analytics, memberId, relationship?.id, workspaceId]);\n\n const submitPlan = async (event: FormEvent<HTMLFormElement>) => {\n event.preventDefault();\n if (!memberId || !programName.trim() || !workoutName.trim()) {\n setError(\n t(\n 'mybodyshield.pages.coaching.planRequired',\n 'Program and first workout names are required.'\n )\n );\n return;\n }\n setError(null);\n setSaved(null);\n try {\n const program = await createProgram.mutateAsync({\n name: programName.trim(),\n memberId,\n coachId: currentUser?.id,\n weeks: 4,\n daysPerWeek: 3,\n });\n await addWorkout.mutateAsync({\n programId: program.id,\n name: workoutName.trim(),\n dayOfWeek: 1,\n weekNumber: 1,\n durationMin: 45,\n });\n if (nutritionName.trim()) {\n await createNutritionPlan.mutateAsync({\n name: nutritionName.trim(),\n memberId,\n coachId: currentUser?.id,\n goalType: 'GENERAL_FITNESS' as MybGoalType,\n calorieTarget: Number(calories) || undefined,\n proteinTarget: Number(protein) || undefined,\n });\n }\n setSaved(t('mybodyshield.pages.coaching.planSaved', 'Plan assigned to client.'));\n setProgramName('');\n setWorkoutName('');\n setNutritionName('');\n setCalories('');\n setProtein('');\n analytics.emitFact('coaching', 'plan_assigned', { workspaceId, memberId });\n } catch (cause) {\n setError(\n cause instanceof Error\n ? cause.message\n : t('mybodyshield.common.unknownError', 'Unknown error')\n );\n }\n };\n\n if (relationshipQuery.isLoading || insights.isLoading) {\n return (\n <LoadingView\n label={t('mybodyshield.pages.coaching.clientLoading', 'Loading client workspace…')}\n />\n );\n }\n if (relationshipQuery.isError || insights.isError) {\n return (\n <ErrorView\n message={t('mybodyshield.pages.coaching.clientError', 'Client data could not be loaded.')}\n onRetry={() => {\n void relationshipQuery.refetch();\n void insights.refetch();\n }}\n />\n );\n }\n if (!memberId || !relationship || !isCoach) {\n return (\n <EmptyState\n illustration=\"empty-people\"\n title={t('mybodyshield.pages.coaching.clientUnavailable', 'Client workspace unavailable')}\n description={t(\n 'mybodyshield.pages.coaching.clientUnavailableDescription',\n 'Only an active coaching relationship with the required consent can open this workspace.'\n )}\n action={\n <Button onClick={() => navigateTo('coaching')}>\n <ArrowLeft className=\"mr-2 h-4 w-4\" />\n {t('mybodyshield.common.back', 'Back')}\n </Button>\n }\n />\n );\n }\n\n return (\n <PageLayout\n title={pageTitle}\n description={t(\n 'mybodyshield.pages.coaching.clientDescription',\n 'Consent-scoped progress and plan tools for client #{{id}}.',\n { id: memberId.slice(0, 8) }\n )}\n actions={\n <Button variant=\"outline\" onClick={() => navigateTo('coaching')}>\n <ArrowLeft className=\"mr-2 h-4 w-4\" />\n {t('mybodyshield.common.back', 'Coaching')}\n </Button>\n }\n >\n {!hasData ? (\n <EmptyState\n illustration=\"empty-data\"\n title={t('mybodyshield.pages.coaching.noAnalytics', 'No client analytics yet')}\n description={t(\n 'mybodyshield.pages.coaching.noAnalyticsDescription',\n 'Analytics appear after the client logs workouts, meals, recovery, or body metrics.'\n )}\n />\n ) : (\n <Section\n title={t('mybodyshield.pages.coaching.analyticsTitle', 'Last 30 days')}\n description={t(\n 'mybodyshield.pages.coaching.analyticsDescription',\n 'Values come from the client’s consented training, nutrition, and progress records.'\n )}\n >\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 xl:grid-cols-4\">\n <MetricsCard\n icon={<Dumbbell className=\"h-5 w-5\" />}\n value={insights.data?.summary.totalSessions ?? 0}\n label={t('mybodyshield.pages.coaching.completedWorkouts', 'Completed workouts')}\n />\n <MetricsCard\n icon={<Salad className=\"h-5 w-5\" />}\n value={formatCoachPercent(insights.data?.summary.macroAdherencePercent ?? 0)}\n label={t('mybodyshield.pages.coaching.nutritionAdherence', 'Nutrition adherence')}\n />\n <MetricsCard\n icon={<HeartPulse className=\"h-5 w-5\" />}\n value={Math.round(insights.data?.summary.averageReadiness ?? 0) || '—'}\n label={t('mybodyshield.pages.coaching.averageReadiness', 'Average readiness')}\n />\n <MetricsCard\n icon={<Activity className=\"h-5 w-5\" />}\n value={insights.data?.summary.consistencyStreakDays ?? 0}\n label={t('mybodyshield.pages.coaching.consistencyStreak', 'Day streak')}\n />\n </div>\n </Section>\n )}\n\n {hasData && (\n <Section\n title={t('mybodyshield.pages.coaching.recentActivity', 'Recent activity')}\n description={t(\n 'mybodyshield.pages.coaching.recentActivityDescription',\n 'Only days with recorded client data are shown.'\n )}\n >\n {recentActivity.length === 0 ? (\n <EmptyState\n title={t('mybodyshield.pages.coaching.noRecentActivity', 'No recent activity')}\n />\n ) : (\n <div className=\"grid gap-3 sm:grid-cols-2 lg:grid-cols-3\">\n {recentActivity.map((day) => (\n <GlassCard key={day.date} treatment=\"plain\" className=\"p-4\">\n <p className=\"text-xs font-medium uppercase tracking-wide text-text-tertiary\">\n {new Date(day.date).toLocaleDateString()}\n </p>\n <div className=\"mt-2 flex flex-wrap gap-x-4 gap-y-1 text-sm text-text-secondary\">\n {day.sessions > 0 && (\n <span>\n {day.sessions} workout{day.sessions === 1 ? '' : 's'}\n </span>\n )}\n {day.weight != null && <span>{day.weight} weight</span>}\n {day.readiness != null && <span>{day.readiness} readiness</span>}\n {day.sleepHours != null && <span>{day.sleepHours}h sleep</span>}\n </div>\n </GlassCard>\n ))}\n </div>\n )}\n </Section>\n )}\n\n <Section\n title={t('mybodyshield.pages.coaching.planBuilderTitle', 'Build a client plan')}\n description={t(\n 'mybodyshield.pages.coaching.planBuilderDescription',\n 'Creates one real training program with its first workout. Nutrition targets are optional and use the existing plan model.'\n )}\n >\n <GlassCard treatment=\"plain\" className=\"max-w-3xl p-5\">\n <form className=\"grid gap-4 sm:grid-cols-2\" onSubmit={(event) => void submitPlan(event)}>\n <div className=\"space-y-2\">\n <Label htmlFor=\"coach-program-name\">\n {t('mybodyshield.pages.coaching.programName', 'Program name')}\n </Label>\n <Input\n id=\"coach-program-name\"\n value={programName}\n onChange={(event) => setProgramName(event.target.value)}\n />\n </div>\n <div className=\"space-y-2\">\n <Label htmlFor=\"coach-workout-name\">\n {t('mybodyshield.pages.coaching.firstWorkout', 'First workout')}\n </Label>\n <Input\n id=\"coach-workout-name\"\n value={workoutName}\n onChange={(event) => setWorkoutName(event.target.value)}\n />\n </div>\n <div className=\"space-y-2\">\n <Label htmlFor=\"coach-nutrition-name\">\n {t('mybodyshield.pages.coaching.nutritionPlanName', 'Nutrition plan (optional)')}\n </Label>\n <Input\n id=\"coach-nutrition-name\"\n value={nutritionName}\n onChange={(event) => setNutritionName(event.target.value)}\n />\n </div>\n <div className=\"grid grid-cols-2 gap-3\">\n <div className=\"space-y-2\">\n <Label htmlFor=\"coach-calories\">\n {t('mybodyshield.pages.coaching.calorieTarget', 'Calories')}\n </Label>\n <Input\n id=\"coach-calories\"\n inputMode=\"numeric\"\n value={calories}\n onChange={(event) => setCalories(event.target.value)}\n />\n </div>\n <div className=\"space-y-2\">\n <Label htmlFor=\"coach-protein\">\n {t('mybodyshield.pages.coaching.proteinTarget', 'Protein g')}\n </Label>\n <Input\n id=\"coach-protein\"\n inputMode=\"numeric\"\n value={protein}\n onChange={(event) => setProtein(event.target.value)}\n />\n </div>\n </div>\n {error && (\n <p className=\"sm:col-span-2 rounded-lg bg-status-error-bg-subtle px-3 py-2 text-sm text-status-error-text\">\n {error}\n </p>\n )}\n {saved && (\n <p className=\"sm:col-span-2 rounded-lg bg-status-success-bg-subtle px-3 py-2 text-sm text-status-success-text\">\n {saved}\n </p>\n )}\n <div className=\"sm:col-span-2\">\n <Button\n type=\"submit\"\n disabled={\n createProgram.isPending || addWorkout.isPending || createNutritionPlan.isPending\n }\n >\n <Plus className=\"mr-2 h-4 w-4\" />\n {t('mybodyshield.pages.coaching.assignPlan', 'Assign plan')}\n </Button>\n </div>\n </form>\n </GlassCard>\n </Section>\n <Section title={t('mybodyshield.pages.coaching.scopeTitle', 'Consent scope')}>\n <GlassCard\n treatment=\"plain\"\n className=\"flex items-center gap-3 p-5 text-sm text-text-secondary\"\n >\n <Target className=\"h-5 w-5 shrink-0 text-status-success-text\" />\n {t(\n 'mybodyshield.pages.coaching.scopeDescription',\n 'This view is available only while the active relationship shares workouts, nutrition, and metrics.'\n )}\n </GlassCard>\n </Section>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CoachingPage.d.ts","sourceRoot":"","sources":["../../../../src/mybodyshield/pages/coaching/CoachingPage.tsx"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;
|
|
1
|
+
{"version":3,"file":"CoachingPage.d.ts","sourceRoot":"","sources":["../../../../src/mybodyshield/pages/coaching/CoachingPage.tsx"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAsxBH,wBAAgB,YAAY,gCAsK3B"}
|
|
@@ -318,11 +318,11 @@ function $({ t: e, label: t, icon: n, on: r }) {
|
|
|
318
318
|
]
|
|
319
319
|
});
|
|
320
320
|
}
|
|
321
|
-
function de({ relationship: e, currentUserId: t, busy: i, onTransition: a }) {
|
|
322
|
-
let
|
|
321
|
+
function de({ relationship: e, currentUserId: t, busy: i, onTransition: a, onOpenClient: o }) {
|
|
322
|
+
let s = c(), l = se(s), u = !!t && e.coachId === t, d = !!t && e.memberId === t, f = l[e.status], m = e.status === "ACTIVE" || e.status === "PAUSED", [y, C] = b(!1), D = _(), O = e.status === "ACTIVE";
|
|
323
323
|
return /* @__PURE__ */ R(B, {
|
|
324
324
|
treatment: "glass",
|
|
325
|
-
glow:
|
|
325
|
+
glow: O,
|
|
326
326
|
className: "flex h-full flex-col gap-4 p-5 transition-shadow hover:shadow-float" + (e.status === "PENDING" ? " ring-1 ring-inset ring-[color-mix(in_oklab,var(--color-status-warning-text)_35%,transparent)]" : ""),
|
|
327
327
|
children: [
|
|
328
328
|
/* @__PURE__ */ R("div", {
|
|
@@ -333,10 +333,10 @@ function de({ relationship: e, currentUserId: t, busy: i, onTransition: a }) {
|
|
|
333
333
|
className: "min-w-0",
|
|
334
334
|
children: [/* @__PURE__ */ L("p", {
|
|
335
335
|
className: "truncate text-sm font-semibold text-text-primary",
|
|
336
|
-
children: e.specialty?.trim() ||
|
|
336
|
+
children: e.specialty?.trim() || s("mybodyshield.pages.coaching.coachingRelationship", "Coaching relationship")
|
|
337
337
|
}), /* @__PURE__ */ L("p", {
|
|
338
338
|
className: "text-xs text-text-tertiary",
|
|
339
|
-
children:
|
|
339
|
+
children: s("mybodyshield.pages.coaching.coachMember", "Coach ↔ member")
|
|
340
340
|
})]
|
|
341
341
|
})]
|
|
342
342
|
}), /* @__PURE__ */ L(r, { status: e.status })]
|
|
@@ -347,34 +347,34 @@ function de({ relationship: e, currentUserId: t, busy: i, onTransition: a }) {
|
|
|
347
347
|
className: "flex items-center gap-2.5 rounded-lg bg-bg-sunken/60 p-2.5",
|
|
348
348
|
children: [/* @__PURE__ */ L("span", {
|
|
349
349
|
"aria-hidden": "true",
|
|
350
|
-
className: `flex size-8 shrink-0 items-center justify-center rounded-full ring-1 ring-inset ${
|
|
350
|
+
className: `flex size-8 shrink-0 items-center justify-center rounded-full ring-1 ring-inset ${u ? "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)]" : "bg-bg-surface text-text-tertiary ring-border-seam"}`,
|
|
351
351
|
children: /* @__PURE__ */ L(I, { className: "h-4 w-4" })
|
|
352
352
|
}), /* @__PURE__ */ R("div", {
|
|
353
353
|
className: "min-w-0",
|
|
354
354
|
children: [/* @__PURE__ */ L("p", {
|
|
355
355
|
className: "text-[11px] font-medium uppercase tracking-wide text-text-tertiary",
|
|
356
|
-
children:
|
|
356
|
+
children: s("mybodyshield.pages.coaching.coachLabel", "Coach")
|
|
357
357
|
}), /* @__PURE__ */ L("p", {
|
|
358
358
|
className: "mt-0.5 truncate text-sm font-medium text-text-primary",
|
|
359
359
|
title: e.coachId,
|
|
360
|
-
children:
|
|
360
|
+
children: u ? s("mybodyshield.pages.coaching.you", "You") : `#${X(e.coachId)}`
|
|
361
361
|
})]
|
|
362
362
|
})]
|
|
363
363
|
}), /* @__PURE__ */ R("div", {
|
|
364
364
|
className: "flex items-center gap-2.5 rounded-lg bg-bg-sunken/60 p-2.5",
|
|
365
365
|
children: [/* @__PURE__ */ L("span", {
|
|
366
366
|
"aria-hidden": "true",
|
|
367
|
-
className: `flex size-8 shrink-0 items-center justify-center rounded-full ring-1 ring-inset ${
|
|
367
|
+
className: `flex size-8 shrink-0 items-center justify-center rounded-full ring-1 ring-inset ${d ? "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)]" : "bg-bg-surface text-text-tertiary ring-border-seam"}`,
|
|
368
368
|
children: /* @__PURE__ */ L(I, { className: "h-4 w-4" })
|
|
369
369
|
}), /* @__PURE__ */ R("div", {
|
|
370
370
|
className: "min-w-0",
|
|
371
371
|
children: [/* @__PURE__ */ L("p", {
|
|
372
372
|
className: "text-[11px] font-medium uppercase tracking-wide text-text-tertiary",
|
|
373
|
-
children:
|
|
373
|
+
children: s("mybodyshield.pages.coaching.memberLabel", "Member")
|
|
374
374
|
}), /* @__PURE__ */ L("p", {
|
|
375
375
|
className: "mt-0.5 truncate text-sm font-medium text-text-primary",
|
|
376
376
|
title: e.memberId,
|
|
377
|
-
children:
|
|
377
|
+
children: d ? s("mybodyshield.pages.coaching.you", "You") : `#${X(e.memberId)}`
|
|
378
378
|
})]
|
|
379
379
|
})]
|
|
380
380
|
})]
|
|
@@ -385,42 +385,42 @@ function de({ relationship: e, currentUserId: t, busy: i, onTransition: a }) {
|
|
|
385
385
|
className: "flex flex-wrap gap-1.5",
|
|
386
386
|
children: [
|
|
387
387
|
/* @__PURE__ */ L($, {
|
|
388
|
-
t:
|
|
389
|
-
label:
|
|
388
|
+
t: s,
|
|
389
|
+
label: s("mybodyshield.pages.coaching.workouts", "Workouts"),
|
|
390
390
|
icon: E,
|
|
391
391
|
on: e.shareWorkouts
|
|
392
392
|
}),
|
|
393
393
|
/* @__PURE__ */ L($, {
|
|
394
|
-
t:
|
|
395
|
-
label:
|
|
394
|
+
t: s,
|
|
395
|
+
label: s("mybodyshield.pages.coaching.nutrition", "Nutrition"),
|
|
396
396
|
icon: F,
|
|
397
397
|
on: e.shareNutrition
|
|
398
398
|
}),
|
|
399
399
|
/* @__PURE__ */ L($, {
|
|
400
|
-
t:
|
|
401
|
-
label:
|
|
400
|
+
t: s,
|
|
401
|
+
label: s("mybodyshield.pages.coaching.metrics", "Metrics"),
|
|
402
402
|
icon: A,
|
|
403
403
|
on: e.shareMetrics
|
|
404
404
|
})
|
|
405
405
|
]
|
|
406
406
|
}), /* @__PURE__ */ L("p", {
|
|
407
407
|
className: "text-xs leading-relaxed text-text-tertiary",
|
|
408
|
-
children: ce(
|
|
408
|
+
children: ce(s, e.status)
|
|
409
409
|
})]
|
|
410
410
|
}),
|
|
411
411
|
(e.startedAt || e.endedAt) && /* @__PURE__ */ R("div", {
|
|
412
412
|
className: "flex flex-wrap items-center gap-x-4 gap-y-1 text-xs text-text-tertiary",
|
|
413
413
|
children: [e.startedAt && /* @__PURE__ */ R("span", {
|
|
414
414
|
className: "inline-flex items-center gap-1",
|
|
415
|
-
children: [/* @__PURE__ */ L(x, { className: "h-3.5 w-3.5" }),
|
|
415
|
+
children: [/* @__PURE__ */ L(x, { className: "h-3.5 w-3.5" }), s("mybodyshield.pages.coaching.started", "Started {{date}}", { date: n(e.startedAt) })]
|
|
416
416
|
}), e.endedAt && /* @__PURE__ */ R("span", {
|
|
417
417
|
className: "inline-flex items-center gap-1",
|
|
418
|
-
children: [/* @__PURE__ */ L(S, { className: "h-3.5 w-3.5" }),
|
|
418
|
+
children: [/* @__PURE__ */ L(S, { className: "h-3.5 w-3.5" }), s("mybodyshield.pages.coaching.ended", "Ended {{date}}", { date: n(e.endedAt) })]
|
|
419
419
|
})]
|
|
420
420
|
}),
|
|
421
|
-
|
|
421
|
+
f.length > 0 && /* @__PURE__ */ L("div", {
|
|
422
422
|
className: "mt-auto flex flex-wrap gap-2 pt-1",
|
|
423
|
-
children:
|
|
423
|
+
children: f.map((t) => /* @__PURE__ */ L(z, {
|
|
424
424
|
size: "sm",
|
|
425
425
|
variant: t.primary ? void 0 : "outline",
|
|
426
426
|
disabled: i,
|
|
@@ -428,22 +428,22 @@ function de({ relationship: e, currentUserId: t, busy: i, onTransition: a }) {
|
|
|
428
428
|
children: t.label
|
|
429
429
|
}, t.to))
|
|
430
430
|
}),
|
|
431
|
-
|
|
432
|
-
className: `border-t border-border-seam pt-3 ${
|
|
431
|
+
m && /* @__PURE__ */ R("div", {
|
|
432
|
+
className: `border-t border-border-seam pt-3 ${f.length === 0 ? "mt-auto" : ""}`,
|
|
433
433
|
children: [/* @__PURE__ */ R("button", {
|
|
434
434
|
type: "button",
|
|
435
|
-
onClick: () =>
|
|
436
|
-
"aria-expanded":
|
|
435
|
+
onClick: () => C((e) => !e),
|
|
436
|
+
"aria-expanded": y,
|
|
437
437
|
className: "inline-flex items-center gap-1.5 text-sm font-medium text-accent hover:underline",
|
|
438
438
|
children: [
|
|
439
439
|
/* @__PURE__ */ L(M, { className: "h-4 w-4" }),
|
|
440
|
-
|
|
441
|
-
L(
|
|
440
|
+
y ? s("mybodyshield.pages.coaching.hideDiscussion", "Hide discussion") : s("mybodyshield.pages.coaching.discussion", "Discussion"),
|
|
441
|
+
L(y ? T : w, { className: "h-4 w-4" })
|
|
442
442
|
]
|
|
443
443
|
}), /* @__PURE__ */ L(h, {
|
|
444
444
|
initial: !1,
|
|
445
|
-
children:
|
|
446
|
-
initial:
|
|
445
|
+
children: y && /* @__PURE__ */ L(g.div, {
|
|
446
|
+
initial: D ? !1 : {
|
|
447
447
|
opacity: 0,
|
|
448
448
|
height: 0
|
|
449
449
|
},
|
|
@@ -451,11 +451,11 @@ function de({ relationship: e, currentUserId: t, busy: i, onTransition: a }) {
|
|
|
451
451
|
opacity: 1,
|
|
452
452
|
height: "auto"
|
|
453
453
|
},
|
|
454
|
-
exit:
|
|
454
|
+
exit: D ? { opacity: 0 } : {
|
|
455
455
|
opacity: 0,
|
|
456
456
|
height: 0
|
|
457
457
|
},
|
|
458
|
-
transition:
|
|
458
|
+
transition: D ? { duration: 0 } : {
|
|
459
459
|
duration: .25,
|
|
460
460
|
ease: Y
|
|
461
461
|
},
|
|
@@ -465,12 +465,18 @@ function de({ relationship: e, currentUserId: t, busy: i, onTransition: a }) {
|
|
|
465
465
|
children: /* @__PURE__ */ L(p, {
|
|
466
466
|
entityType: "mybodyshield-coaching",
|
|
467
467
|
entityId: e.id,
|
|
468
|
-
title:
|
|
469
|
-
description:
|
|
468
|
+
title: s("mybodyshield.pages.coaching.coachingDiscussion", "Coaching discussion"),
|
|
469
|
+
description: s("mybodyshield.pages.coaching.coachingDiscussionDescription", "A private thread between the coach and member.")
|
|
470
470
|
})
|
|
471
471
|
})
|
|
472
472
|
}, "discussion")
|
|
473
473
|
})]
|
|
474
|
+
}),
|
|
475
|
+
O && u && /* @__PURE__ */ L(z, {
|
|
476
|
+
variant: "outline",
|
|
477
|
+
size: "sm",
|
|
478
|
+
onClick: () => o(e.memberId),
|
|
479
|
+
children: s("mybodyshield.pages.coaching.openClient", "Open client workspace")
|
|
474
480
|
})
|
|
475
481
|
]
|
|
476
482
|
});
|
|
@@ -506,92 +512,93 @@ function fe() {
|
|
|
506
512
|
});
|
|
507
513
|
}
|
|
508
514
|
function pe() {
|
|
509
|
-
let n = c(), { currentUser: r } = e(), o = _(), [
|
|
515
|
+
let n = c(), { currentUser: r } = e(), { navigateTo: o } = e(), d = _(), [p, m] = b("ALL"), [h, v] = b(!1), [x, S] = b(null), [C, w] = b(null), T = a(), E = s(), D = T.data?.items ?? [], O = y(() => {
|
|
510
516
|
let e = {
|
|
511
517
|
PENDING: 0,
|
|
512
518
|
ACTIVE: 0,
|
|
513
519
|
PAUSED: 0,
|
|
514
520
|
ENDED: 0
|
|
515
521
|
};
|
|
516
|
-
for (let t of
|
|
522
|
+
for (let t of D) e[t.status] += 1;
|
|
517
523
|
return e;
|
|
518
|
-
}, [
|
|
524
|
+
}, [D]), A = y(() => [{
|
|
519
525
|
value: "ALL",
|
|
520
526
|
label: n("mybodyshield.common.all", "All"),
|
|
521
|
-
count:
|
|
527
|
+
count: D.length
|
|
522
528
|
}, ...J.map((e) => ({
|
|
523
529
|
value: e,
|
|
524
530
|
label: oe(n, e),
|
|
525
|
-
count:
|
|
531
|
+
count: O[e]
|
|
526
532
|
}))], [
|
|
527
|
-
|
|
528
|
-
|
|
533
|
+
D.length,
|
|
534
|
+
O,
|
|
529
535
|
n
|
|
530
|
-
]),
|
|
531
|
-
|
|
536
|
+
]), j = y(() => p === "ALL" ? D : D.filter((e) => e.status === p), [D, p]), M = async (e, t) => {
|
|
537
|
+
S(e), w(null);
|
|
532
538
|
try {
|
|
533
|
-
await
|
|
539
|
+
await E.mutateAsync({
|
|
534
540
|
id: e,
|
|
535
541
|
status: t
|
|
536
542
|
});
|
|
537
543
|
} catch (e) {
|
|
538
|
-
|
|
544
|
+
w(e instanceof Error ? e.message : n("mybodyshield.pages.coaching.failedToUpdate", "Failed to update the relationship."));
|
|
539
545
|
} finally {
|
|
540
|
-
|
|
546
|
+
S(null);
|
|
541
547
|
}
|
|
542
|
-
},
|
|
548
|
+
}, N = T.isLoading && !T.data;
|
|
543
549
|
return /* @__PURE__ */ R(t, {
|
|
544
550
|
title: n("mybodyshield.pages.coaching.title", "Coaching"),
|
|
545
551
|
description: n("mybodyshield.pages.coaching.description", "Coach ↔ member relationships and what's shared."),
|
|
546
552
|
actions: /* @__PURE__ */ R(z, {
|
|
547
|
-
onClick: () =>
|
|
553
|
+
onClick: () => v(!0),
|
|
548
554
|
children: [/* @__PURE__ */ L(P, { className: "mr-2 h-4 w-4" }), n("mybodyshield.pages.coaching.newCoaching", "New coaching")]
|
|
549
555
|
}),
|
|
550
556
|
children: [
|
|
551
557
|
/* @__PURE__ */ L("div", {
|
|
552
558
|
className: "mb-6",
|
|
553
559
|
children: /* @__PURE__ */ L(f, {
|
|
554
|
-
options:
|
|
555
|
-
value:
|
|
556
|
-
onChange:
|
|
560
|
+
options: A,
|
|
561
|
+
value: p,
|
|
562
|
+
onChange: m
|
|
557
563
|
})
|
|
558
564
|
}),
|
|
559
|
-
|
|
565
|
+
C && /* @__PURE__ */ L("p", {
|
|
560
566
|
className: "mb-4 rounded-lg bg-status-error-bg-subtle px-3 py-2 text-sm text-status-error-text",
|
|
561
|
-
children:
|
|
567
|
+
children: C
|
|
562
568
|
}),
|
|
563
|
-
|
|
569
|
+
N ? /* @__PURE__ */ L(u, { label: n("mybodyshield.pages.coaching.loading", "Loading coaching relationships…") }) : T.isError ? /* @__PURE__ */ L(l, {
|
|
564
570
|
message: n("mybodyshield.pages.coaching.error", "We couldn't load your coaching relationships."),
|
|
565
|
-
onRetry: () => void
|
|
566
|
-
}) :
|
|
571
|
+
onRetry: () => void T.refetch()
|
|
572
|
+
}) : j.length === 0 ? /* @__PURE__ */ L(i, {
|
|
567
573
|
illustration: "empty-data",
|
|
568
574
|
icon: /* @__PURE__ */ L(k, { className: "h-10 w-10" }),
|
|
569
|
-
title:
|
|
570
|
-
description:
|
|
575
|
+
title: D.length === 0 ? n("mybodyshield.pages.coaching.emptyTitle", "No coaching relationships") : n("mybodyshield.pages.coaching.emptyFilteredTitle", "No relationships match this filter"),
|
|
576
|
+
description: D.length === 0 ? n("mybodyshield.pages.coaching.emptyDescription", "Connect a coach and a member to start sharing training, nutrition and progress data.") : n("mybodyshield.pages.coaching.emptyFilteredDescription", "Try a different status filter, or create a new coaching relationship."),
|
|
571
577
|
action: /* @__PURE__ */ R(z, {
|
|
572
|
-
onClick: () =>
|
|
578
|
+
onClick: () => v(!0),
|
|
573
579
|
children: [/* @__PURE__ */ L(P, { className: "mr-2 h-4 w-4" }), n("mybodyshield.pages.coaching.newCoaching", "New coaching")]
|
|
574
580
|
})
|
|
575
581
|
}) : /* @__PURE__ */ L(g.div, {
|
|
576
582
|
className: "grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3",
|
|
577
|
-
variants:
|
|
578
|
-
initial:
|
|
583
|
+
variants: d ? void 0 : ie,
|
|
584
|
+
initial: d ? !1 : "hidden",
|
|
579
585
|
animate: "show",
|
|
580
|
-
children:
|
|
586
|
+
children: j.map((e) => /* @__PURE__ */ L(g.div, {
|
|
581
587
|
className: "h-full",
|
|
582
|
-
variants:
|
|
588
|
+
variants: d ? void 0 : ae,
|
|
583
589
|
children: /* @__PURE__ */ L(de, {
|
|
584
590
|
relationship: e,
|
|
585
591
|
currentUserId: r?.id,
|
|
586
|
-
busy:
|
|
587
|
-
onTransition: (e, t) => void
|
|
592
|
+
busy: x === e.id,
|
|
593
|
+
onTransition: (e, t) => void M(e, t),
|
|
594
|
+
onOpenClient: (e) => o(`coaching/${e}`)
|
|
588
595
|
})
|
|
589
596
|
}, e.id))
|
|
590
597
|
}),
|
|
591
598
|
/* @__PURE__ */ L(fe, {}),
|
|
592
599
|
/* @__PURE__ */ L(ue, {
|
|
593
|
-
open:
|
|
594
|
-
onOpenChange:
|
|
600
|
+
open: h,
|
|
601
|
+
onOpenChange: v,
|
|
595
602
|
defaultMember: {
|
|
596
603
|
userId: r?.id ?? "",
|
|
597
604
|
displayName: r?.name || r?.email || ""
|