@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.
@@ -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":";;;;;;;;;;;;;;;;;;AAyBA,IAAM,IAAa;AAEnB,SAAS,IAAY;CACnB,IAAM,oBAAM,IAAI,MAAM,EAChB,IAAQ,IAAI,KAAK,EAAI;AAE3B,QADA,EAAM,QAAQ,EAAI,SAAS,GAAG,IAAa,EAAE,EACtC;EAAE,gBAAgB,EAAM,aAAa;EAAE,cAAc,EAAI,aAAa;EAAE;;AAGjF,SAAgB,IAAkB;CAChC,IAAM,IAAI,GAAO,EACX,EAAE,gBAAa,GAAiC,EAChD,EAAE,gBAAa,gBAAa,kBAAe,GAAiB,EAC5D,IAAY,GAA0B,EACtC,KAAQ,SAAc,GAAW,EAAE,EAAE,CAAC,EACtC,IAAoB,GAAe,UAAU,EAAS,EACtD,IAAe,EAAkB,MAAM,MAAM,IAC7C,IAAW,EAAe;EAAE,GAAG;EAAO;EAAU,CAAC,EACjD,IAAgB,GAAqB,EACrC,IAAa,GAAkB,EAC/B,IAAsB,IAA2B,EACjD,CAAC,GAAa,KAAkB,EAAS,GAAG,EAC5C,CAAC,GAAa,KAAkB,EAAS,GAAG,EAC5C,CAAC,GAAe,KAAoB,EAAS,GAAG,EAChD,CAAC,GAAU,KAAe,EAAS,GAAG,EACtC,CAAC,GAAS,KAAc,EAAS,GAAG,EACpC,CAAC,GAAO,KAAY,EAAwB,KAAK,EACjD,CAAC,GAAO,KAAY,EAAwB,KAAK,EAEjD,KAAU,GAAc,YAAY,GAAa,IACjD,IAAU,GAAsB,EAAS,KAAK,EAC9C,KAAkB,EAAS,MAAM,SAAS,EAAE,EAC/C,QACE,MACC,EAAI,WAAW,KAAK,EAAI,UAAU,QAAQ,EAAI,aAAa,QAAQ,EAAI,cAAc,KACxF,CACA,MAAM,GAAG,CACT,SAAS,EACN,KAAY,EAAE,2CAA2C,mBAAmB;AAElF,UAAgB;AACd,EAAI,GAAc,MAAM,KACtB,EAAU,aAAa,mBAAmB;GACxC;GACA,UAAU,EAAa;GACvB;GACD,CAAC;IAEH;EAAC;EAAW;EAAU,GAAc;EAAI;EAAY,CAAC;CAExD,IAAM,KAAa,OAAO,MAAsC;AAE9D,MADA,EAAM,gBAAgB,EAClB,CAAC,KAAY,CAAC,EAAY,MAAM,IAAI,CAAC,EAAY,MAAM,EAAE;AAC3D,KACE,EACE,4CACA,gDACD,CACF;AACD;;AAGF,EADA,EAAS,KAAK,EACd,EAAS,KAAK;AACd,MAAI;GACF,IAAM,IAAU,MAAM,EAAc,YAAY;IAC9C,MAAM,EAAY,MAAM;IACxB;IACA,SAAS,GAAa;IACtB,OAAO;IACP,aAAa;IACd,CAAC;AAwBF,GAvBA,MAAM,EAAW,YAAY;IAC3B,WAAW,EAAQ;IACnB,MAAM,EAAY,MAAM;IACxB,WAAW;IACX,YAAY;IACZ,aAAa;IACd,CAAC,EACE,EAAc,MAAM,IACtB,MAAM,EAAoB,YAAY;IACpC,MAAM,EAAc,MAAM;IAC1B;IACA,SAAS,GAAa;IACtB,UAAU;IACV,eAAe,OAAO,EAAS,IAAI,KAAA;IACnC,eAAe,OAAO,EAAQ,IAAI,KAAA;IACnC,CAAC,EAEJ,EAAS,EAAE,yCAAyC,2BAA2B,CAAC,EAChF,EAAe,GAAG,EAClB,EAAe,GAAG,EAClB,EAAiB,GAAG,EACpB,EAAY,GAAG,EACf,EAAW,GAAG,EACd,EAAU,SAAS,YAAY,iBAAiB;IAAE;IAAa;IAAU,CAAC;WACnE,GAAO;AACd,KACE,aAAiB,QACb,EAAM,UACN,EAAE,oCAAoC,gBAAgB,CAC3D;;;AAyCL,QArCI,EAAkB,aAAa,EAAS,YAExC,kBAAC,GAAD,EACE,OAAO,EAAE,6CAA6C,4BAA4B,EAClF,CAAA,GAGF,EAAkB,WAAW,EAAS,UAEtC,kBAAC,GAAD;EACE,SAAS,EAAE,2CAA2C,mCAAmC;EACzF,eAAe;AAER,GADA,EAAkB,SAAS,EAC3B,EAAS,SAAS;;EAEzB,CAAA,GAGF,CAAC,KAAY,CAAC,KAAgB,CAAC,KAE/B,kBAAC,GAAD;EACE,cAAa;EACb,OAAO,EAAE,iDAAiD,+BAA+B;EACzF,aAAa,EACX,4DACA,0FACD;EACD,QACE,kBAAC,GAAD;GAAQ,eAAe,EAAW,WAAW;aAA7C,CACE,kBAAC,GAAD,EAAW,WAAU,gBAAiB,CAAA,EACrC,EAAE,4BAA4B,OAAO,CAC/B;;EAEX,CAAA,GAKJ,kBAAC,GAAD;EACE,OAAO;EACP,aAAa,EACX,iDACA,8DACA,EAAE,IAAI,EAAS,MAAM,GAAG,EAAE,EAAE,CAC7B;EACD,SACE,kBAAC,GAAD;GAAQ,SAAQ;GAAU,eAAe,EAAW,WAAW;aAA/D,CACE,kBAAC,GAAD,EAAW,WAAU,gBAAiB,CAAA,EACrC,EAAE,4BAA4B,WAAW,CACnC;;YAXb;GAcI,IAUA,kBAAC,GAAD;IACE,OAAO,EAAE,8CAA8C,eAAe;IACtE,aAAa,EACX,oDACA,qFACD;cAED,kBAAC,OAAD;KAAK,WAAU;eAAf;MACE,kBAAC,GAAD;OACE,MAAM,kBAAC,GAAD,EAAU,WAAU,WAAY,CAAA;OACtC,OAAO,EAAS,MAAM,QAAQ,iBAAiB;OAC/C,OAAO,EAAE,iDAAiD,qBAAqB;OAC/E,CAAA;MACF,kBAAC,GAAD;OACE,MAAM,kBAAC,GAAD,EAAO,WAAU,WAAY,CAAA;OACnC,OAAO,GAAmB,EAAS,MAAM,QAAQ,yBAAyB,EAAE;OAC5E,OAAO,EAAE,kDAAkD,sBAAsB;OACjF,CAAA;MACF,kBAAC,GAAD;OACE,MAAM,kBAAC,GAAD,EAAY,WAAU,WAAY,CAAA;OACxC,OAAO,KAAK,MAAM,EAAS,MAAM,QAAQ,oBAAoB,EAAE,IAAI;OACnE,OAAO,EAAE,gDAAgD,oBAAoB;OAC7E,CAAA;MACF,kBAAC,GAAD;OACE,MAAM,kBAAC,GAAD,EAAU,WAAU,WAAY,CAAA;OACtC,OAAO,EAAS,MAAM,QAAQ,yBAAyB;OACvD,OAAO,EAAE,iDAAiD,aAAa;OACvE,CAAA;MACE;;IACE,CAAA,GAtCV,kBAAC,GAAD;IACE,cAAa;IACb,OAAO,EAAE,2CAA2C,0BAA0B;IAC9E,aAAa,EACX,sDACA,qFACD;IACD,CAAA;GAkCH,KACC,kBAAC,GAAD;IACE,OAAO,EAAE,8CAA8C,kBAAkB;IACzE,aAAa,EACX,yDACA,iDACD;cAEA,EAAe,WAAW,IACzB,kBAAC,GAAD,EACE,OAAO,EAAE,gDAAgD,qBAAqB,EAC9E,CAAA,GAEF,kBAAC,OAAD;KAAK,WAAU;eACZ,EAAe,KAAK,MACnB,kBAAC,GAAD;MAA0B,WAAU;MAAQ,WAAU;gBAAtD,CACE,kBAAC,KAAD;OAAG,WAAU;iBACV,IAAI,KAAK,EAAI,KAAK,CAAC,oBAAoB;OACtC,CAAA,EACJ,kBAAC,OAAD;OAAK,WAAU;iBAAf;QACG,EAAI,WAAW,KACd,kBAAC,QAAD,EAAA,UAAA;SACG,EAAI;SAAS;SAAS,EAAI,aAAa,IAAI,KAAK;SAC5C,EAAA,CAAA;QAER,EAAI,UAAU,QAAQ,kBAAC,QAAD,EAAA,UAAA,CAAO,EAAI,QAAO,UAAc,EAAA,CAAA;QACtD,EAAI,aAAa,QAAQ,kBAAC,QAAD,EAAA,UAAA,CAAO,EAAI,WAAU,aAAiB,EAAA,CAAA;QAC/D,EAAI,cAAc,QAAQ,kBAAC,QAAD,EAAA,UAAA,CAAO,EAAI,YAAW,UAAc,EAAA,CAAA;QAC3D;SACI;QAdI,EAAI,KAcR,CACZ;KACE,CAAA;IAEA,CAAA;GAGZ,kBAAC,GAAD;IACE,OAAO,EAAE,gDAAgD,sBAAsB;IAC/E,aAAa,EACX,sDACA,4HACD;cAED,kBAAC,GAAD;KAAW,WAAU;KAAQ,WAAU;eACrC,kBAAC,QAAD;MAAM,WAAU;MAA4B,WAAW,MAAU,KAAK,GAAW,EAAM;gBAAvF;OACE,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,GAAD;SAAO,SAAQ;mBACZ,EAAE,2CAA2C,eAAe;SACvD,CAAA,EACR,kBAAC,GAAD;SACE,IAAG;SACH,OAAO;SACP,WAAW,MAAU,EAAe,EAAM,OAAO,MAAM;SACvD,CAAA,CACE;;OACN,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,GAAD;SAAO,SAAQ;mBACZ,EAAE,4CAA4C,gBAAgB;SACzD,CAAA,EACR,kBAAC,GAAD;SACE,IAAG;SACH,OAAO;SACP,WAAW,MAAU,EAAe,EAAM,OAAO,MAAM;SACvD,CAAA,CACE;;OACN,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,GAAD;SAAO,SAAQ;mBACZ,EAAE,iDAAiD,4BAA4B;SAC1E,CAAA,EACR,kBAAC,GAAD;SACE,IAAG;SACH,OAAO;SACP,WAAW,MAAU,EAAiB,EAAM,OAAO,MAAM;SACzD,CAAA,CACE;;OACN,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,GAAD;UAAO,SAAQ;oBACZ,EAAE,6CAA6C,WAAW;UACrD,CAAA,EACR,kBAAC,GAAD;UACE,IAAG;UACH,WAAU;UACV,OAAO;UACP,WAAW,MAAU,EAAY,EAAM,OAAO,MAAM;UACpD,CAAA,CACE;YACN,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,GAAD;UAAO,SAAQ;oBACZ,EAAE,6CAA6C,YAAY;UACtD,CAAA,EACR,kBAAC,GAAD;UACE,IAAG;UACH,WAAU;UACV,OAAO;UACP,WAAW,MAAU,EAAW,EAAM,OAAO,MAAM;UACnD,CAAA,CACE;WACF;;OACL,KACC,kBAAC,KAAD;QAAG,WAAU;kBACV;QACC,CAAA;OAEL,KACC,kBAAC,KAAD;QAAG,WAAU;kBACV;QACC,CAAA;OAEN,kBAAC,OAAD;QAAK,WAAU;kBACb,kBAAC,GAAD;SACE,MAAK;SACL,UACE,EAAc,aAAa,EAAW,aAAa,EAAoB;mBAH3E,CAME,kBAAC,IAAD,EAAM,WAAU,gBAAiB,CAAA,EAChC,EAAE,0CAA0C,cAAc,CACpD;;QACL,CAAA;OACD;;KACG,CAAA;IACJ,CAAA;GACV,kBAAC,GAAD;IAAS,OAAO,EAAE,0CAA0C,gBAAgB;cAC1E,kBAAC,GAAD;KACE,WAAU;KACV,WAAU;eAFZ,CAIE,kBAAC,IAAD,EAAQ,WAAU,6CAA8C,CAAA,EAC/D,EACC,gDACA,qGACD,CACS;;IACJ,CAAA;GACC"}
@@ -1 +1 @@
1
- {"version":3,"file":"CoachingPage.d.ts","sourceRoot":"","sources":["../../../../src/mybodyshield/pages/coaching/CoachingPage.tsx"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AA+wBH,wBAAgB,YAAY,gCAoK3B"}
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 o = c(), s = se(o), l = !!t && e.coachId === t, u = !!t && e.memberId === t, d = s[e.status], f = e.status === "ACTIVE" || e.status === "PAUSED", [m, y] = b(!1), C = _();
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: e.status === "ACTIVE",
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() || o("mybodyshield.pages.coaching.coachingRelationship", "Coaching relationship")
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: o("mybodyshield.pages.coaching.coachMember", "Coach ↔ member")
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 ${l ? "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"}`,
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: o("mybodyshield.pages.coaching.coachLabel", "Coach")
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: l ? o("mybodyshield.pages.coaching.you", "You") : `#${X(e.coachId)}`
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 ${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"}`,
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: o("mybodyshield.pages.coaching.memberLabel", "Member")
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: u ? o("mybodyshield.pages.coaching.you", "You") : `#${X(e.memberId)}`
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: o,
389
- label: o("mybodyshield.pages.coaching.workouts", "Workouts"),
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: o,
395
- label: o("mybodyshield.pages.coaching.nutrition", "Nutrition"),
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: o,
401
- label: o("mybodyshield.pages.coaching.metrics", "Metrics"),
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(o, e.status)
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" }), o("mybodyshield.pages.coaching.started", "Started {{date}}", { date: n(e.startedAt) })]
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" }), o("mybodyshield.pages.coaching.ended", "Ended {{date}}", { date: n(e.endedAt) })]
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
- d.length > 0 && /* @__PURE__ */ L("div", {
421
+ f.length > 0 && /* @__PURE__ */ L("div", {
422
422
  className: "mt-auto flex flex-wrap gap-2 pt-1",
423
- children: d.map((t) => /* @__PURE__ */ L(z, {
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
- f && /* @__PURE__ */ R("div", {
432
- className: `border-t border-border-seam pt-3 ${d.length === 0 ? "mt-auto" : ""}`,
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: () => y((e) => !e),
436
- "aria-expanded": m,
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
- m ? o("mybodyshield.pages.coaching.hideDiscussion", "Hide discussion") : o("mybodyshield.pages.coaching.discussion", "Discussion"),
441
- L(m ? T : w, { className: "h-4 w-4" })
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: m && /* @__PURE__ */ L(g.div, {
446
- initial: C ? !1 : {
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: C ? { opacity: 0 } : {
454
+ exit: D ? { opacity: 0 } : {
455
455
  opacity: 0,
456
456
  height: 0
457
457
  },
458
- transition: C ? { duration: 0 } : {
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: o("mybodyshield.pages.coaching.coachingDiscussion", "Coaching discussion"),
469
- description: o("mybodyshield.pages.coaching.coachingDiscussionDescription", "A private thread between the coach and member.")
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 = _(), [d, p] = b("ALL"), [m, h] = b(!1), [v, x] = b(null), [S, C] = b(null), w = a(), T = s(), E = w.data?.items ?? [], D = y(() => {
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 E) e[t.status] += 1;
522
+ for (let t of D) e[t.status] += 1;
517
523
  return e;
518
- }, [E]), O = y(() => [{
524
+ }, [D]), A = y(() => [{
519
525
  value: "ALL",
520
526
  label: n("mybodyshield.common.all", "All"),
521
- count: E.length
527
+ count: D.length
522
528
  }, ...J.map((e) => ({
523
529
  value: e,
524
530
  label: oe(n, e),
525
- count: D[e]
531
+ count: O[e]
526
532
  }))], [
527
- E.length,
528
- D,
533
+ D.length,
534
+ O,
529
535
  n
530
- ]), A = y(() => d === "ALL" ? E : E.filter((e) => e.status === d), [E, d]), j = async (e, t) => {
531
- x(e), C(null);
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 T.mutateAsync({
539
+ await E.mutateAsync({
534
540
  id: e,
535
541
  status: t
536
542
  });
537
543
  } catch (e) {
538
- C(e instanceof Error ? e.message : n("mybodyshield.pages.coaching.failedToUpdate", "Failed to update the relationship."));
544
+ w(e instanceof Error ? e.message : n("mybodyshield.pages.coaching.failedToUpdate", "Failed to update the relationship."));
539
545
  } finally {
540
- x(null);
546
+ S(null);
541
547
  }
542
- }, M = w.isLoading && !w.data;
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: () => h(!0),
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: O,
555
- value: d,
556
- onChange: p
560
+ options: A,
561
+ value: p,
562
+ onChange: m
557
563
  })
558
564
  }),
559
- S && /* @__PURE__ */ L("p", {
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: S
567
+ children: C
562
568
  }),
563
- M ? /* @__PURE__ */ L(u, { label: n("mybodyshield.pages.coaching.loading", "Loading coaching relationships…") }) : w.isError ? /* @__PURE__ */ L(l, {
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 w.refetch()
566
- }) : A.length === 0 ? /* @__PURE__ */ L(i, {
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: E.length === 0 ? n("mybodyshield.pages.coaching.emptyTitle", "No coaching relationships") : n("mybodyshield.pages.coaching.emptyFilteredTitle", "No relationships match this filter"),
570
- description: E.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."),
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: () => h(!0),
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: o ? void 0 : ie,
578
- initial: o ? !1 : "hidden",
583
+ variants: d ? void 0 : ie,
584
+ initial: d ? !1 : "hidden",
579
585
  animate: "show",
580
- children: A.map((e) => /* @__PURE__ */ L(g.div, {
586
+ children: j.map((e) => /* @__PURE__ */ L(g.div, {
581
587
  className: "h-full",
582
- variants: o ? void 0 : ae,
588
+ variants: d ? void 0 : ae,
583
589
  children: /* @__PURE__ */ L(de, {
584
590
  relationship: e,
585
591
  currentUserId: r?.id,
586
- busy: v === e.id,
587
- onTransition: (e, t) => void j(e, t)
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: m,
594
- onOpenChange: h,
600
+ open: h,
601
+ onOpenChange: v,
595
602
  defaultMember: {
596
603
  userId: r?.id ?? "",
597
604
  displayName: r?.name || r?.email || ""