@burdenoff/microfe-mybodyshield 2026.820.1 → 2026.821.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/MyBodyShieldRoot.d.ts.map +1 -1
- package/dist/mybodyshield/MyBodyShieldRoot.js +68 -44
- package/dist/mybodyshield/MyBodyShieldRoot.js.map +1 -1
- package/dist/mybodyshield/MyBodyShieldRoutes.d.ts.map +1 -1
- package/dist/mybodyshield/MyBodyShieldRoutes.js +37 -16
- package/dist/mybodyshield/MyBodyShieldRoutes.js.map +1 -1
- package/dist/mybodyshield/hooks/useMyBodyShieldApi.d.ts +11 -1
- package/dist/mybodyshield/hooks/useMyBodyShieldApi.d.ts.map +1 -1
- package/dist/mybodyshield/hooks/useMyBodyShieldApi.js +113 -46
- package/dist/mybodyshield/hooks/useMyBodyShieldApi.js.map +1 -1
- package/dist/mybodyshield/pages/SettingsPage.d.ts +10 -0
- package/dist/mybodyshield/pages/SettingsPage.d.ts.map +1 -0
- package/dist/mybodyshield/pages/SettingsPage.js +104 -0
- package/dist/mybodyshield/pages/SettingsPage.js.map +1 -0
- package/dist/mybodyshield/pages/ai/AiControlsPage.d.ts +10 -0
- package/dist/mybodyshield/pages/ai/AiControlsPage.d.ts.map +1 -0
- package/dist/mybodyshield/pages/ai/AiControlsPage.js +378 -0
- package/dist/mybodyshield/pages/ai/AiControlsPage.js.map +1 -0
- package/dist/mybodyshield/pages/insights/InsightsCharts.d.ts +18 -0
- package/dist/mybodyshield/pages/insights/InsightsCharts.d.ts.map +1 -0
- package/dist/mybodyshield/pages/insights/InsightsCharts.js +549 -0
- package/dist/mybodyshield/pages/insights/InsightsCharts.js.map +1 -0
- package/dist/mybodyshield/pages/insights/InsightsPage.d.ts +12 -0
- package/dist/mybodyshield/pages/insights/InsightsPage.d.ts.map +1 -0
- package/dist/mybodyshield/pages/insights/InsightsPage.js +317 -0
- package/dist/mybodyshield/pages/insights/InsightsPage.js.map +1 -0
- package/dist/mybodyshield/types/index.d.ts +75 -0
- package/dist/mybodyshield/types/index.d.ts.map +1 -1
- package/dist/mybodyshield/utils/pageContext.d.ts.map +1 -1
- package/dist/mybodyshield/utils/pageContext.js +4 -1
- package/dist/mybodyshield/utils/pageContext.js.map +1 -1
- package/dist/node_modules/recharts/es6/cartesian/Line.js +473 -0
- package/dist/node_modules/recharts/es6/cartesian/Line.js.map +1 -0
- package/dist/node_modules/recharts/es6/cartesian/Scatter.js +368 -0
- package/dist/node_modules/recharts/es6/cartesian/Scatter.js.map +1 -0
- package/dist/node_modules/recharts/es6/cartesian/ZAxis.js +103 -0
- package/dist/node_modules/recharts/es6/cartesian/ZAxis.js.map +1 -0
- package/dist/node_modules/recharts/es6/chart/BarChart.js +24 -0
- package/dist/node_modules/recharts/es6/chart/BarChart.js.map +1 -0
- package/dist/node_modules/recharts/es6/chart/ComposedChart.js +38 -0
- package/dist/node_modules/recharts/es6/chart/ComposedChart.js.map +1 -0
- package/dist/node_modules/recharts/es6/util/DataUtils.js +12 -2
- package/dist/node_modules/recharts/es6/util/DataUtils.js.map +1 -1
- package/dist/node_modules/recharts/es6/util/ScatterUtils.js +48 -0
- package/dist/node_modules/recharts/es6/util/ScatterUtils.js.map +1 -0
- package/dist/stories/InsightsCharts.stories.d.ts +15 -0
- package/dist/stories/InsightsCharts.stories.d.ts.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,549 @@
|
|
|
1
|
+
import { cn as e } from "../../utils/cn.js";
|
|
2
|
+
import { formatDate as t, formatGrams as n, formatKcal as r, formatNumber as i, formatWeight as a } from "../../utils/formatters.js";
|
|
3
|
+
import { IconChip as o, MACRO_HUE as s } from "../_shared/visuals.js";
|
|
4
|
+
import { Legend as c } from "../../../node_modules/recharts/es6/component/Legend.js";
|
|
5
|
+
import { Tooltip as l } from "../../../node_modules/recharts/es6/component/Tooltip.js";
|
|
6
|
+
import { ResponsiveContainer as u } from "../../../node_modules/recharts/es6/component/ResponsiveContainer.js";
|
|
7
|
+
import { Bar as d } from "../../../node_modules/recharts/es6/cartesian/Bar.js";
|
|
8
|
+
import { CartesianGrid as f } from "../../../node_modules/recharts/es6/cartesian/CartesianGrid.js";
|
|
9
|
+
import { Line as p } from "../../../node_modules/recharts/es6/cartesian/Line.js";
|
|
10
|
+
import { Area as m } from "../../../node_modules/recharts/es6/cartesian/Area.js";
|
|
11
|
+
import { XAxis as h } from "../../../node_modules/recharts/es6/cartesian/XAxis.js";
|
|
12
|
+
import { YAxis as g } from "../../../node_modules/recharts/es6/cartesian/YAxis.js";
|
|
13
|
+
import { BarChart as _ } from "../../../node_modules/recharts/es6/chart/BarChart.js";
|
|
14
|
+
import { ComposedChart as v } from "../../../node_modules/recharts/es6/chart/ComposedChart.js";
|
|
15
|
+
import { useMemo as y } from "react";
|
|
16
|
+
import { Activity as b, Dumbbell as x, HeartPulse as S, Moon as C, Scale as w, Utensils as T } from "lucide-react";
|
|
17
|
+
import { jsx as E, jsxs as D } from "react/jsx-runtime";
|
|
18
|
+
import { GlassCard as O } from "@burdenoff/fe-libs/ui";
|
|
19
|
+
//#region src/mybodyshield/pages/insights/InsightsCharts.tsx
|
|
20
|
+
var k = {
|
|
21
|
+
primary: "var(--color-action-primary-bg)",
|
|
22
|
+
secondary: "var(--color-accent-primary)",
|
|
23
|
+
success: "var(--color-status-success-text)",
|
|
24
|
+
warning: "var(--color-status-warning-text)",
|
|
25
|
+
error: "var(--color-status-error-text)",
|
|
26
|
+
chart1: "var(--color-chart-1)",
|
|
27
|
+
chart2: "var(--color-chart-2)",
|
|
28
|
+
chart3: "var(--color-chart-3)",
|
|
29
|
+
chart4: "var(--color-chart-4)"
|
|
30
|
+
}, A = {
|
|
31
|
+
background: "var(--color-bg-surface)",
|
|
32
|
+
border: "1px solid var(--color-border-seam)",
|
|
33
|
+
borderRadius: 12,
|
|
34
|
+
color: "var(--color-text-primary)",
|
|
35
|
+
fontSize: 12
|
|
36
|
+
}, j = {
|
|
37
|
+
stroke: "var(--color-border-seam)",
|
|
38
|
+
strokeDasharray: "4 4"
|
|
39
|
+
}, M = {
|
|
40
|
+
fill: "var(--color-text-tertiary)",
|
|
41
|
+
fontSize: 12
|
|
42
|
+
};
|
|
43
|
+
function N(e) {
|
|
44
|
+
return y(() => [...e].sort((e, t) => new Date(e.date).getTime() - new Date(t.date).getTime()).map((e) => ({
|
|
45
|
+
label: t(e.date),
|
|
46
|
+
date: e.date,
|
|
47
|
+
sessions: e.sessions,
|
|
48
|
+
volumeKg: e.volumeKg,
|
|
49
|
+
readiness: e.readiness ?? null,
|
|
50
|
+
sleepHours: e.sleepHours ?? null,
|
|
51
|
+
calories: e.calories,
|
|
52
|
+
proteinG: e.proteinG,
|
|
53
|
+
carbsG: e.carbsG,
|
|
54
|
+
fatG: e.fatG,
|
|
55
|
+
weight: e.weight ?? null
|
|
56
|
+
})), [e]);
|
|
57
|
+
}
|
|
58
|
+
function P({ message: e = "No data for the selected range" }) {
|
|
59
|
+
return /* @__PURE__ */ D("div", {
|
|
60
|
+
className: "flex h-[240px] flex-col items-center justify-center gap-2 text-center",
|
|
61
|
+
children: [/* @__PURE__ */ E(b, { className: "h-8 w-8 text-text-tertiary" }), /* @__PURE__ */ E("p", {
|
|
62
|
+
className: "text-sm text-text-secondary",
|
|
63
|
+
children: e
|
|
64
|
+
})]
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
function F({ title: t, icon: n, children: r, className: i }) {
|
|
68
|
+
return /* @__PURE__ */ D(O, {
|
|
69
|
+
treatment: "plain",
|
|
70
|
+
className: e("overflow-hidden p-5", i),
|
|
71
|
+
children: [/* @__PURE__ */ D("div", {
|
|
72
|
+
className: "mb-4 flex items-center gap-2.5",
|
|
73
|
+
children: [n, /* @__PURE__ */ E("h3", {
|
|
74
|
+
className: "text-sm font-semibold text-text-primary",
|
|
75
|
+
children: t
|
|
76
|
+
})]
|
|
77
|
+
}), r]
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
function I({ daily: e }) {
|
|
81
|
+
let t = N(e);
|
|
82
|
+
return t.length === 0 ? /* @__PURE__ */ E(F, {
|
|
83
|
+
title: "Training volume",
|
|
84
|
+
icon: /* @__PURE__ */ E(o, {
|
|
85
|
+
icon: /* @__PURE__ */ E(x, { className: "h-4 w-4" }),
|
|
86
|
+
tint: k.primary
|
|
87
|
+
}),
|
|
88
|
+
children: /* @__PURE__ */ E(P, { message: "Log completed sessions to see volume trends." })
|
|
89
|
+
}) : /* @__PURE__ */ E(F, {
|
|
90
|
+
title: "Training volume",
|
|
91
|
+
icon: /* @__PURE__ */ E(o, {
|
|
92
|
+
icon: /* @__PURE__ */ E(x, { className: "h-4 w-4" }),
|
|
93
|
+
tint: k.primary
|
|
94
|
+
}),
|
|
95
|
+
children: /* @__PURE__ */ E("div", {
|
|
96
|
+
className: "h-[260px] w-full",
|
|
97
|
+
children: /* @__PURE__ */ E(u, {
|
|
98
|
+
width: "100%",
|
|
99
|
+
height: "100%",
|
|
100
|
+
children: /* @__PURE__ */ D(v, {
|
|
101
|
+
data: t,
|
|
102
|
+
margin: {
|
|
103
|
+
top: 4,
|
|
104
|
+
right: 16,
|
|
105
|
+
bottom: 4,
|
|
106
|
+
left: 4
|
|
107
|
+
},
|
|
108
|
+
children: [
|
|
109
|
+
/* @__PURE__ */ E(f, {
|
|
110
|
+
...j,
|
|
111
|
+
vertical: !1
|
|
112
|
+
}),
|
|
113
|
+
/* @__PURE__ */ E(h, {
|
|
114
|
+
dataKey: "label",
|
|
115
|
+
tickLine: !1,
|
|
116
|
+
axisLine: !1,
|
|
117
|
+
tick: M,
|
|
118
|
+
tickMargin: 8,
|
|
119
|
+
minTickGap: 24
|
|
120
|
+
}),
|
|
121
|
+
/* @__PURE__ */ E(g, {
|
|
122
|
+
yAxisId: "left",
|
|
123
|
+
tickLine: !1,
|
|
124
|
+
axisLine: !1,
|
|
125
|
+
tick: M,
|
|
126
|
+
width: 52,
|
|
127
|
+
tickFormatter: (e) => a(Number(e))
|
|
128
|
+
}),
|
|
129
|
+
/* @__PURE__ */ E(g, {
|
|
130
|
+
yAxisId: "right",
|
|
131
|
+
orientation: "right",
|
|
132
|
+
tickLine: !1,
|
|
133
|
+
axisLine: !1,
|
|
134
|
+
tick: M,
|
|
135
|
+
width: 36,
|
|
136
|
+
allowDecimals: !1
|
|
137
|
+
}),
|
|
138
|
+
/* @__PURE__ */ E(l, {
|
|
139
|
+
contentStyle: A,
|
|
140
|
+
labelStyle: { color: "var(--color-text-tertiary)" },
|
|
141
|
+
formatter: (e, t) => t === "Volume" ? [a(Number(e)), t] : [i(Number(e)), "Sessions"]
|
|
142
|
+
}),
|
|
143
|
+
/* @__PURE__ */ E(c, { wrapperStyle: {
|
|
144
|
+
fontSize: 12,
|
|
145
|
+
paddingTop: 8
|
|
146
|
+
} }),
|
|
147
|
+
/* @__PURE__ */ E(d, {
|
|
148
|
+
yAxisId: "left",
|
|
149
|
+
dataKey: "volumeKg",
|
|
150
|
+
name: "Volume",
|
|
151
|
+
fill: k.primary,
|
|
152
|
+
radius: [
|
|
153
|
+
4,
|
|
154
|
+
4,
|
|
155
|
+
0,
|
|
156
|
+
0
|
|
157
|
+
],
|
|
158
|
+
opacity: .85
|
|
159
|
+
}),
|
|
160
|
+
/* @__PURE__ */ E(p, {
|
|
161
|
+
yAxisId: "right",
|
|
162
|
+
type: "monotone",
|
|
163
|
+
dataKey: "sessions",
|
|
164
|
+
name: "Sessions",
|
|
165
|
+
stroke: k.secondary,
|
|
166
|
+
strokeWidth: 2.5,
|
|
167
|
+
dot: !1,
|
|
168
|
+
activeDot: {
|
|
169
|
+
r: 4,
|
|
170
|
+
strokeWidth: 2,
|
|
171
|
+
stroke: "var(--color-bg-surface)"
|
|
172
|
+
}
|
|
173
|
+
})
|
|
174
|
+
]
|
|
175
|
+
})
|
|
176
|
+
})
|
|
177
|
+
})
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
function L({ daily: e }) {
|
|
181
|
+
let t = N(e);
|
|
182
|
+
return t.some((e) => e.readiness != null || e.sleepHours != null) ? /* @__PURE__ */ E(F, {
|
|
183
|
+
title: "Recovery & sleep",
|
|
184
|
+
icon: /* @__PURE__ */ E(o, {
|
|
185
|
+
icon: /* @__PURE__ */ E(S, { className: "h-4 w-4" }),
|
|
186
|
+
tint: k.success
|
|
187
|
+
}),
|
|
188
|
+
children: /* @__PURE__ */ E("div", {
|
|
189
|
+
className: "h-[260px] w-full",
|
|
190
|
+
children: /* @__PURE__ */ E(u, {
|
|
191
|
+
width: "100%",
|
|
192
|
+
height: "100%",
|
|
193
|
+
children: /* @__PURE__ */ D(v, {
|
|
194
|
+
data: t,
|
|
195
|
+
margin: {
|
|
196
|
+
top: 4,
|
|
197
|
+
right: 16,
|
|
198
|
+
bottom: 4,
|
|
199
|
+
left: 4
|
|
200
|
+
},
|
|
201
|
+
children: [
|
|
202
|
+
/* @__PURE__ */ E(f, {
|
|
203
|
+
...j,
|
|
204
|
+
vertical: !1
|
|
205
|
+
}),
|
|
206
|
+
/* @__PURE__ */ E(h, {
|
|
207
|
+
dataKey: "label",
|
|
208
|
+
tickLine: !1,
|
|
209
|
+
axisLine: !1,
|
|
210
|
+
tick: M,
|
|
211
|
+
tickMargin: 8,
|
|
212
|
+
minTickGap: 24
|
|
213
|
+
}),
|
|
214
|
+
/* @__PURE__ */ E(g, {
|
|
215
|
+
yAxisId: "left",
|
|
216
|
+
tickLine: !1,
|
|
217
|
+
axisLine: !1,
|
|
218
|
+
tick: M,
|
|
219
|
+
width: 36,
|
|
220
|
+
domain: [0, 100],
|
|
221
|
+
hide: !t.some((e) => e.readiness != null)
|
|
222
|
+
}),
|
|
223
|
+
/* @__PURE__ */ E(g, {
|
|
224
|
+
yAxisId: "right",
|
|
225
|
+
orientation: "right",
|
|
226
|
+
tickLine: !1,
|
|
227
|
+
axisLine: !1,
|
|
228
|
+
tick: M,
|
|
229
|
+
width: 44,
|
|
230
|
+
tickFormatter: (e) => `${i(Number(e))} h`,
|
|
231
|
+
hide: !t.some((e) => e.sleepHours != null)
|
|
232
|
+
}),
|
|
233
|
+
/* @__PURE__ */ E(l, {
|
|
234
|
+
contentStyle: A,
|
|
235
|
+
labelStyle: { color: "var(--color-text-tertiary)" },
|
|
236
|
+
formatter: (e, t) => t === "Readiness" ? [i(Number(e)), t] : [`${i(Number(e))} h`, t]
|
|
237
|
+
}),
|
|
238
|
+
/* @__PURE__ */ E(c, { wrapperStyle: {
|
|
239
|
+
fontSize: 12,
|
|
240
|
+
paddingTop: 8
|
|
241
|
+
} }),
|
|
242
|
+
/* @__PURE__ */ E(m, {
|
|
243
|
+
yAxisId: "right",
|
|
244
|
+
type: "monotone",
|
|
245
|
+
dataKey: "sleepHours",
|
|
246
|
+
name: "Sleep",
|
|
247
|
+
stroke: k.secondary,
|
|
248
|
+
strokeWidth: 2,
|
|
249
|
+
fill: k.secondary,
|
|
250
|
+
fillOpacity: .12,
|
|
251
|
+
dot: !1,
|
|
252
|
+
activeDot: {
|
|
253
|
+
r: 4,
|
|
254
|
+
strokeWidth: 2,
|
|
255
|
+
stroke: "var(--color-bg-surface)"
|
|
256
|
+
},
|
|
257
|
+
connectNulls: !0
|
|
258
|
+
}),
|
|
259
|
+
/* @__PURE__ */ E(p, {
|
|
260
|
+
yAxisId: "left",
|
|
261
|
+
type: "monotone",
|
|
262
|
+
dataKey: "readiness",
|
|
263
|
+
name: "Readiness",
|
|
264
|
+
stroke: k.success,
|
|
265
|
+
strokeWidth: 2.5,
|
|
266
|
+
dot: !1,
|
|
267
|
+
activeDot: {
|
|
268
|
+
r: 4,
|
|
269
|
+
strokeWidth: 2,
|
|
270
|
+
stroke: "var(--color-bg-surface)"
|
|
271
|
+
},
|
|
272
|
+
connectNulls: !0
|
|
273
|
+
})
|
|
274
|
+
]
|
|
275
|
+
})
|
|
276
|
+
})
|
|
277
|
+
})
|
|
278
|
+
}) : /* @__PURE__ */ E(F, {
|
|
279
|
+
title: "Recovery & sleep",
|
|
280
|
+
icon: /* @__PURE__ */ E(o, {
|
|
281
|
+
icon: /* @__PURE__ */ E(S, { className: "h-4 w-4" }),
|
|
282
|
+
tint: k.success
|
|
283
|
+
}),
|
|
284
|
+
children: /* @__PURE__ */ E(P, { message: "Log recovery entries to see readiness and sleep trends." })
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
function R({ daily: e }) {
|
|
288
|
+
let t = N(e);
|
|
289
|
+
return t.length === 0 ? /* @__PURE__ */ E(F, {
|
|
290
|
+
title: "Macro adherence",
|
|
291
|
+
icon: /* @__PURE__ */ E(o, {
|
|
292
|
+
icon: /* @__PURE__ */ E(T, { className: "h-4 w-4" }),
|
|
293
|
+
tint: k.warning
|
|
294
|
+
}),
|
|
295
|
+
children: /* @__PURE__ */ E(P, { message: "Log meals to see macro trends." })
|
|
296
|
+
}) : /* @__PURE__ */ E(F, {
|
|
297
|
+
title: "Macro adherence",
|
|
298
|
+
icon: /* @__PURE__ */ E(o, {
|
|
299
|
+
icon: /* @__PURE__ */ E(T, { className: "h-4 w-4" }),
|
|
300
|
+
tint: k.warning
|
|
301
|
+
}),
|
|
302
|
+
children: /* @__PURE__ */ E("div", {
|
|
303
|
+
className: "h-[260px] w-full",
|
|
304
|
+
children: /* @__PURE__ */ E(u, {
|
|
305
|
+
width: "100%",
|
|
306
|
+
height: "100%",
|
|
307
|
+
children: /* @__PURE__ */ D(_, {
|
|
308
|
+
data: t,
|
|
309
|
+
margin: {
|
|
310
|
+
top: 4,
|
|
311
|
+
right: 16,
|
|
312
|
+
bottom: 4,
|
|
313
|
+
left: 4
|
|
314
|
+
},
|
|
315
|
+
children: [
|
|
316
|
+
/* @__PURE__ */ E(f, {
|
|
317
|
+
...j,
|
|
318
|
+
vertical: !1
|
|
319
|
+
}),
|
|
320
|
+
/* @__PURE__ */ E(h, {
|
|
321
|
+
dataKey: "label",
|
|
322
|
+
tickLine: !1,
|
|
323
|
+
axisLine: !1,
|
|
324
|
+
tick: M,
|
|
325
|
+
tickMargin: 8,
|
|
326
|
+
minTickGap: 24
|
|
327
|
+
}),
|
|
328
|
+
/* @__PURE__ */ E(g, {
|
|
329
|
+
tickLine: !1,
|
|
330
|
+
axisLine: !1,
|
|
331
|
+
tick: M,
|
|
332
|
+
width: 44,
|
|
333
|
+
tickFormatter: (e) => n(Number(e))
|
|
334
|
+
}),
|
|
335
|
+
/* @__PURE__ */ E(l, {
|
|
336
|
+
contentStyle: A,
|
|
337
|
+
labelStyle: { color: "var(--color-text-tertiary)" },
|
|
338
|
+
formatter: (e, t) => [n(Number(e)), t]
|
|
339
|
+
}),
|
|
340
|
+
/* @__PURE__ */ E(c, { wrapperStyle: {
|
|
341
|
+
fontSize: 12,
|
|
342
|
+
paddingTop: 8
|
|
343
|
+
} }),
|
|
344
|
+
/* @__PURE__ */ E(d, {
|
|
345
|
+
dataKey: "proteinG",
|
|
346
|
+
name: "Protein",
|
|
347
|
+
stackId: "macros",
|
|
348
|
+
fill: s.protein
|
|
349
|
+
}),
|
|
350
|
+
/* @__PURE__ */ E(d, {
|
|
351
|
+
dataKey: "carbsG",
|
|
352
|
+
name: "Carbs",
|
|
353
|
+
stackId: "macros",
|
|
354
|
+
fill: s.carbs
|
|
355
|
+
}),
|
|
356
|
+
/* @__PURE__ */ E(d, {
|
|
357
|
+
dataKey: "fatG",
|
|
358
|
+
name: "Fat",
|
|
359
|
+
stackId: "macros",
|
|
360
|
+
fill: s.fat
|
|
361
|
+
})
|
|
362
|
+
]
|
|
363
|
+
})
|
|
364
|
+
})
|
|
365
|
+
})
|
|
366
|
+
});
|
|
367
|
+
}
|
|
368
|
+
function z({ daily: e }) {
|
|
369
|
+
let t = N(e);
|
|
370
|
+
return t.length === 0 ? /* @__PURE__ */ E(F, {
|
|
371
|
+
title: "Calorie trend",
|
|
372
|
+
icon: /* @__PURE__ */ E(o, {
|
|
373
|
+
icon: /* @__PURE__ */ E(w, { className: "h-4 w-4" }),
|
|
374
|
+
tint: k.chart1
|
|
375
|
+
}),
|
|
376
|
+
children: /* @__PURE__ */ E(P, { message: "Log meals to see calorie trends." })
|
|
377
|
+
}) : /* @__PURE__ */ E(F, {
|
|
378
|
+
title: "Calorie trend",
|
|
379
|
+
icon: /* @__PURE__ */ E(o, {
|
|
380
|
+
icon: /* @__PURE__ */ E(w, { className: "h-4 w-4" }),
|
|
381
|
+
tint: k.chart1
|
|
382
|
+
}),
|
|
383
|
+
children: /* @__PURE__ */ E("div", {
|
|
384
|
+
className: "h-[260px] w-full",
|
|
385
|
+
children: /* @__PURE__ */ E(u, {
|
|
386
|
+
width: "100%",
|
|
387
|
+
height: "100%",
|
|
388
|
+
children: /* @__PURE__ */ D(v, {
|
|
389
|
+
data: t,
|
|
390
|
+
margin: {
|
|
391
|
+
top: 4,
|
|
392
|
+
right: 16,
|
|
393
|
+
bottom: 4,
|
|
394
|
+
left: 4
|
|
395
|
+
},
|
|
396
|
+
children: [
|
|
397
|
+
/* @__PURE__ */ E(f, {
|
|
398
|
+
...j,
|
|
399
|
+
vertical: !1
|
|
400
|
+
}),
|
|
401
|
+
/* @__PURE__ */ E(h, {
|
|
402
|
+
dataKey: "label",
|
|
403
|
+
tickLine: !1,
|
|
404
|
+
axisLine: !1,
|
|
405
|
+
tick: M,
|
|
406
|
+
tickMargin: 8,
|
|
407
|
+
minTickGap: 24
|
|
408
|
+
}),
|
|
409
|
+
/* @__PURE__ */ E(g, {
|
|
410
|
+
tickLine: !1,
|
|
411
|
+
axisLine: !1,
|
|
412
|
+
tick: M,
|
|
413
|
+
width: 52,
|
|
414
|
+
tickFormatter: (e) => r(Number(e))
|
|
415
|
+
}),
|
|
416
|
+
/* @__PURE__ */ E(l, {
|
|
417
|
+
contentStyle: A,
|
|
418
|
+
labelStyle: { color: "var(--color-text-tertiary)" },
|
|
419
|
+
formatter: (e) => [r(Number(e)), "Calories"]
|
|
420
|
+
}),
|
|
421
|
+
/* @__PURE__ */ E(m, {
|
|
422
|
+
type: "monotone",
|
|
423
|
+
dataKey: "calories",
|
|
424
|
+
name: "Calories",
|
|
425
|
+
stroke: k.chart1,
|
|
426
|
+
strokeWidth: 2,
|
|
427
|
+
fill: k.chart1,
|
|
428
|
+
fillOpacity: .12,
|
|
429
|
+
dot: !1,
|
|
430
|
+
activeDot: {
|
|
431
|
+
r: 4,
|
|
432
|
+
strokeWidth: 2,
|
|
433
|
+
stroke: "var(--color-bg-surface)"
|
|
434
|
+
}
|
|
435
|
+
})
|
|
436
|
+
]
|
|
437
|
+
})
|
|
438
|
+
})
|
|
439
|
+
})
|
|
440
|
+
});
|
|
441
|
+
}
|
|
442
|
+
function B(e, t) {
|
|
443
|
+
let n = e.map((e, n) => [e, t[n]]).filter(([e, t]) => e != null && t != null);
|
|
444
|
+
if (n.length < 3) return {
|
|
445
|
+
r: null,
|
|
446
|
+
n: n.length
|
|
447
|
+
};
|
|
448
|
+
let r = n.length, i = n.reduce((e, [t]) => e + t, 0), a = n.reduce((e, [, t]) => e + t, 0), o = n.reduce((e, [t]) => e + t * t, 0), s = n.reduce((e, [, t]) => e + t * t, 0), c = n.reduce((e, [t, n]) => e + t * n, 0), l = Math.sqrt((r * o - i * i) * (r * s - a * a));
|
|
449
|
+
return !Number.isFinite(l) || l === 0 ? {
|
|
450
|
+
r: null,
|
|
451
|
+
n: r
|
|
452
|
+
} : {
|
|
453
|
+
r: (r * c - i * a) / l,
|
|
454
|
+
n: r
|
|
455
|
+
};
|
|
456
|
+
}
|
|
457
|
+
function V(e) {
|
|
458
|
+
return y(() => {
|
|
459
|
+
let t = e.map((e) => e.readiness ?? null), n = e.map((e) => e.sleepHours ?? null), r = e.map((e) => e.volumeKg), i = e.map((e) => e.sessions), a = e.map((e) => e.proteinG), o = e.map((e) => e.calories), s = (e, t, n, r, i) => {
|
|
460
|
+
let { r: a, n: o } = B(r, i);
|
|
461
|
+
return {
|
|
462
|
+
label: e,
|
|
463
|
+
x: t,
|
|
464
|
+
y: n,
|
|
465
|
+
r: a,
|
|
466
|
+
n: o
|
|
467
|
+
};
|
|
468
|
+
};
|
|
469
|
+
return [
|
|
470
|
+
s("Sleep → Readiness", "Sleep", "Readiness", n, t),
|
|
471
|
+
s("Volume → Readiness", "Volume", "Readiness", r, t),
|
|
472
|
+
s("Sessions → Sleep", "Sessions", "Sleep", i, n),
|
|
473
|
+
s("Protein → Readiness", "Protein", "Readiness", a, t),
|
|
474
|
+
s("Calories → Volume", "Calories", "Volume", o, r)
|
|
475
|
+
];
|
|
476
|
+
}, [e]);
|
|
477
|
+
}
|
|
478
|
+
function H(e) {
|
|
479
|
+
let t = Math.abs(e);
|
|
480
|
+
return t >= .7 ? "Strong" : t >= .4 ? "Moderate" : t >= .2 ? "Weak" : "None";
|
|
481
|
+
}
|
|
482
|
+
function U(e) {
|
|
483
|
+
return e > 0 ? "var(--color-status-success-text)" : e < 0 ? "var(--color-status-error-text)" : "var(--color-text-tertiary)";
|
|
484
|
+
}
|
|
485
|
+
function W({ daily: e }) {
|
|
486
|
+
let t = V(e).filter((e) => e.r != null);
|
|
487
|
+
return t.length === 0 ? /* @__PURE__ */ D(O, {
|
|
488
|
+
treatment: "plain",
|
|
489
|
+
className: "p-6",
|
|
490
|
+
children: [/* @__PURE__ */ D("div", {
|
|
491
|
+
className: "flex items-center gap-2.5",
|
|
492
|
+
children: [/* @__PURE__ */ E(o, {
|
|
493
|
+
icon: /* @__PURE__ */ E(C, { className: "h-4 w-4" }),
|
|
494
|
+
tint: k.secondary
|
|
495
|
+
}), /* @__PURE__ */ E("h3", {
|
|
496
|
+
className: "text-sm font-semibold text-text-primary",
|
|
497
|
+
children: "Correlations"
|
|
498
|
+
})]
|
|
499
|
+
}), /* @__PURE__ */ E("p", {
|
|
500
|
+
className: "mt-3 text-sm text-text-secondary",
|
|
501
|
+
children: "Log a few more recovery, training and nutrition days to surface correlations."
|
|
502
|
+
})]
|
|
503
|
+
}) : /* @__PURE__ */ D(O, {
|
|
504
|
+
treatment: "plain",
|
|
505
|
+
className: "overflow-hidden p-0",
|
|
506
|
+
children: [/* @__PURE__ */ D("div", {
|
|
507
|
+
className: "border-b border-border-seam px-5 py-4",
|
|
508
|
+
children: [/* @__PURE__ */ D("div", {
|
|
509
|
+
className: "flex items-center gap-2.5",
|
|
510
|
+
children: [/* @__PURE__ */ E(o, {
|
|
511
|
+
icon: /* @__PURE__ */ E(C, { className: "h-4 w-4" }),
|
|
512
|
+
tint: k.secondary
|
|
513
|
+
}), /* @__PURE__ */ E("h3", {
|
|
514
|
+
className: "text-sm font-semibold text-text-primary",
|
|
515
|
+
children: "Correlations"
|
|
516
|
+
})]
|
|
517
|
+
}), /* @__PURE__ */ E("p", {
|
|
518
|
+
className: "mt-1 text-xs text-text-secondary",
|
|
519
|
+
children: "Pearson correlation across the selected range. More data gives more reliable signals."
|
|
520
|
+
})]
|
|
521
|
+
}), /* @__PURE__ */ E("div", {
|
|
522
|
+
className: "divide-y divide-border-seam/60",
|
|
523
|
+
children: t.map((e) => /* @__PURE__ */ D("div", {
|
|
524
|
+
className: "flex items-center justify-between px-5 py-3",
|
|
525
|
+
children: [/* @__PURE__ */ D("div", { children: [/* @__PURE__ */ E("p", {
|
|
526
|
+
className: "text-sm font-medium text-text-primary",
|
|
527
|
+
children: e.label
|
|
528
|
+
}), /* @__PURE__ */ D("p", {
|
|
529
|
+
className: "text-xs text-text-tertiary",
|
|
530
|
+
children: [e.n, " days with both metrics"]
|
|
531
|
+
})] }), /* @__PURE__ */ D("div", {
|
|
532
|
+
className: "text-right",
|
|
533
|
+
children: [/* @__PURE__ */ E("p", {
|
|
534
|
+
className: "text-sm font-semibold tabular-nums",
|
|
535
|
+
style: { color: U(e.r ?? 0) },
|
|
536
|
+
children: (e.r ?? 0).toFixed(2)
|
|
537
|
+
}), /* @__PURE__ */ E("p", {
|
|
538
|
+
className: "text-xs text-text-tertiary",
|
|
539
|
+
children: H(e.r ?? 0)
|
|
540
|
+
})]
|
|
541
|
+
})]
|
|
542
|
+
}, e.label))
|
|
543
|
+
})]
|
|
544
|
+
});
|
|
545
|
+
}
|
|
546
|
+
//#endregion
|
|
547
|
+
export { z as CalorieChart, W as CorrelationView, R as MacroChart, L as ReadinessSleepChart, I as VolumeChart };
|
|
548
|
+
|
|
549
|
+
//# sourceMappingURL=InsightsCharts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"InsightsCharts.js","names":[],"sources":["../../../../src/mybodyshield/pages/insights/InsightsCharts.tsx"],"sourcesContent":["/**\n * Insights visualisations — recharts-based charts and correlation summary for\n * the MyBodyShield Insights page.\n * @module mybodyshield/pages/insights\n *\n * BOFF-4056: charts are pure presentational components that receive the\n * `MybInsightsDay[]` series already aggregated by the backend. No network\n * calls here.\n */\n\nimport { useMemo, type ReactNode } from 'react';\nimport {\n ResponsiveContainer,\n ComposedChart,\n BarChart,\n Bar,\n Area,\n Line,\n XAxis,\n YAxis,\n CartesianGrid,\n Tooltip,\n Legend,\n} from 'recharts';\nimport { Dumbbell, HeartPulse, Moon, Utensils, Scale, Activity } from 'lucide-react';\nimport { GlassCard } from '@burdenoff/fe-libs/ui';\nimport { cn } from '../../utils/cn';\nimport {\n formatDate,\n formatNumber,\n formatWeight,\n formatGrams,\n formatKcal,\n} from '../../utils/formatters';\nimport { IconChip, MACRO_HUE } from '../_shared/visuals';\nimport type { MybInsightsDay } from '../../types';\n\n// ============================================================================\n// Tokens\n// ============================================================================\n\nconst CHART_COLORS = {\n primary: 'var(--color-action-primary-bg)',\n secondary: 'var(--color-accent-primary)',\n success: 'var(--color-status-success-text)',\n warning: 'var(--color-status-warning-text)',\n error: 'var(--color-status-error-text)',\n chart1: 'var(--color-chart-1)',\n chart2: 'var(--color-chart-2)',\n chart3: 'var(--color-chart-3)',\n chart4: 'var(--color-chart-4)',\n} as const;\n\nconst TOOLTIP_STYLE = {\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\nconst GRID_STYLE = {\n stroke: 'var(--color-border-seam)',\n strokeDasharray: '4 4',\n};\n\nconst AXIS_TICK_STYLE = { fill: 'var(--color-text-tertiary)', fontSize: 12 };\n\n// ============================================================================\n// Chart data shaping\n// ============================================================================\n\ntype ChartRow = {\n label: string;\n date: string;\n sessions: number;\n volumeKg: number;\n readiness: number | null;\n sleepHours: number | null;\n calories: number;\n proteinG: number;\n carbsG: number;\n fatG: number;\n weight: number | null;\n};\n\nfunction useChartData(daily: MybInsightsDay[]): ChartRow[] {\n return useMemo(\n () =>\n [...daily]\n .sort((a, b) => new Date(a.date).getTime() - new Date(b.date).getTime())\n .map((d) => ({\n label: formatDate(d.date),\n date: d.date,\n sessions: d.sessions,\n volumeKg: d.volumeKg,\n readiness: d.readiness ?? null,\n sleepHours: d.sleepHours ?? null,\n calories: d.calories,\n proteinG: d.proteinG,\n carbsG: d.carbsG,\n fatG: d.fatG,\n weight: d.weight ?? null,\n })),\n [daily]\n );\n}\n\nfunction EmptyChart({ message = 'No data for the selected range' }: { message?: string }) {\n return (\n <div className=\"flex h-[240px] flex-col items-center justify-center gap-2 text-center\">\n <Activity className=\"h-8 w-8 text-text-tertiary\" />\n <p className=\"text-sm text-text-secondary\">{message}</p>\n </div>\n );\n}\n\nfunction ChartCard({\n title,\n icon,\n children,\n className,\n}: {\n title: string;\n icon: ReactNode;\n children: ReactNode;\n className?: string;\n}) {\n return (\n <GlassCard treatment=\"plain\" className={cn('overflow-hidden p-5', className)}>\n <div className=\"mb-4 flex items-center gap-2.5\">\n {icon}\n <h3 className=\"text-sm font-semibold text-text-primary\">{title}</h3>\n </div>\n {children}\n </GlassCard>\n );\n}\n\n// ============================================================================\n// Volume + sessions over time\n// ============================================================================\n\nexport function VolumeChart({ daily }: { daily: MybInsightsDay[] }) {\n const data = useChartData(daily);\n\n if (data.length === 0) {\n return (\n <ChartCard\n title=\"Training volume\"\n icon={<IconChip icon={<Dumbbell className=\"h-4 w-4\" />} tint={CHART_COLORS.primary} />}\n >\n <EmptyChart message=\"Log completed sessions to see volume trends.\" />\n </ChartCard>\n );\n }\n\n return (\n <ChartCard\n title=\"Training volume\"\n icon={<IconChip icon={<Dumbbell className=\"h-4 w-4\" />} tint={CHART_COLORS.primary} />}\n >\n <div className=\"h-[260px] w-full\">\n <ResponsiveContainer width=\"100%\" height=\"100%\">\n <ComposedChart data={data} margin={{ top: 4, right: 16, bottom: 4, left: 4 }}>\n <CartesianGrid {...GRID_STYLE} vertical={false} />\n <XAxis\n dataKey=\"label\"\n tickLine={false}\n axisLine={false}\n tick={AXIS_TICK_STYLE}\n tickMargin={8}\n minTickGap={24}\n />\n <YAxis\n yAxisId=\"left\"\n tickLine={false}\n axisLine={false}\n tick={AXIS_TICK_STYLE}\n width={52}\n tickFormatter={(v) => formatWeight(Number(v))}\n />\n <YAxis\n yAxisId=\"right\"\n orientation=\"right\"\n tickLine={false}\n axisLine={false}\n tick={AXIS_TICK_STYLE}\n width={36}\n allowDecimals={false}\n />\n <Tooltip\n contentStyle={TOOLTIP_STYLE}\n labelStyle={{ color: 'var(--color-text-tertiary)' }}\n formatter={(value, name) => {\n if (name === 'Volume') return [formatWeight(Number(value)), name];\n return [formatNumber(Number(value)), 'Sessions'];\n }}\n />\n <Legend wrapperStyle={{ fontSize: 12, paddingTop: 8 }} />\n <Bar\n yAxisId=\"left\"\n dataKey=\"volumeKg\"\n name=\"Volume\"\n fill={CHART_COLORS.primary}\n radius={[4, 4, 0, 0]}\n opacity={0.85}\n />\n <Line\n yAxisId=\"right\"\n type=\"monotone\"\n dataKey=\"sessions\"\n name=\"Sessions\"\n stroke={CHART_COLORS.secondary}\n strokeWidth={2.5}\n dot={false}\n activeDot={{ r: 4, strokeWidth: 2, stroke: 'var(--color-bg-surface)' }}\n />\n </ComposedChart>\n </ResponsiveContainer>\n </div>\n </ChartCard>\n );\n}\n\n// ============================================================================\n// Readiness + sleep over time\n// ============================================================================\n\nexport function ReadinessSleepChart({ daily }: { daily: MybInsightsDay[] }) {\n const data = useChartData(daily);\n const hasData = data.some((d) => d.readiness != null || d.sleepHours != null);\n\n if (!hasData) {\n return (\n <ChartCard\n title=\"Recovery & sleep\"\n icon={<IconChip icon={<HeartPulse className=\"h-4 w-4\" />} tint={CHART_COLORS.success} />}\n >\n <EmptyChart message=\"Log recovery entries to see readiness and sleep trends.\" />\n </ChartCard>\n );\n }\n\n return (\n <ChartCard\n title=\"Recovery & sleep\"\n icon={<IconChip icon={<HeartPulse className=\"h-4 w-4\" />} tint={CHART_COLORS.success} />}\n >\n <div className=\"h-[260px] w-full\">\n <ResponsiveContainer width=\"100%\" height=\"100%\">\n <ComposedChart data={data} margin={{ top: 4, right: 16, bottom: 4, left: 4 }}>\n <CartesianGrid {...GRID_STYLE} vertical={false} />\n <XAxis\n dataKey=\"label\"\n tickLine={false}\n axisLine={false}\n tick={AXIS_TICK_STYLE}\n tickMargin={8}\n minTickGap={24}\n />\n <YAxis\n yAxisId=\"left\"\n tickLine={false}\n axisLine={false}\n tick={AXIS_TICK_STYLE}\n width={36}\n domain={[0, 100]}\n hide={!data.some((d) => d.readiness != null)}\n />\n <YAxis\n yAxisId=\"right\"\n orientation=\"right\"\n tickLine={false}\n axisLine={false}\n tick={AXIS_TICK_STYLE}\n width={44}\n tickFormatter={(v) => `${formatNumber(Number(v))} h`}\n hide={!data.some((d) => d.sleepHours != null)}\n />\n <Tooltip\n contentStyle={TOOLTIP_STYLE}\n labelStyle={{ color: 'var(--color-text-tertiary)' }}\n formatter={(value, name) => {\n if (name === 'Readiness') return [formatNumber(Number(value)), name];\n return [`${formatNumber(Number(value))} h`, name];\n }}\n />\n <Legend wrapperStyle={{ fontSize: 12, paddingTop: 8 }} />\n <Area\n yAxisId=\"right\"\n type=\"monotone\"\n dataKey=\"sleepHours\"\n name=\"Sleep\"\n stroke={CHART_COLORS.secondary}\n strokeWidth={2}\n fill={CHART_COLORS.secondary}\n fillOpacity={0.12}\n dot={false}\n activeDot={{ r: 4, strokeWidth: 2, stroke: 'var(--color-bg-surface)' }}\n connectNulls\n />\n <Line\n yAxisId=\"left\"\n type=\"monotone\"\n dataKey=\"readiness\"\n name=\"Readiness\"\n stroke={CHART_COLORS.success}\n strokeWidth={2.5}\n dot={false}\n activeDot={{ r: 4, strokeWidth: 2, stroke: 'var(--color-bg-surface)' }}\n connectNulls\n />\n </ComposedChart>\n </ResponsiveContainer>\n </div>\n </ChartCard>\n );\n}\n\n// ============================================================================\n// Macros over time\n// ============================================================================\n\nexport function MacroChart({ daily }: { daily: MybInsightsDay[] }) {\n const data = useChartData(daily);\n\n if (data.length === 0) {\n return (\n <ChartCard\n title=\"Macro adherence\"\n icon={<IconChip icon={<Utensils className=\"h-4 w-4\" />} tint={CHART_COLORS.warning} />}\n >\n <EmptyChart message=\"Log meals to see macro trends.\" />\n </ChartCard>\n );\n }\n\n return (\n <ChartCard\n title=\"Macro adherence\"\n icon={<IconChip icon={<Utensils className=\"h-4 w-4\" />} tint={CHART_COLORS.warning} />}\n >\n <div className=\"h-[260px] w-full\">\n <ResponsiveContainer width=\"100%\" height=\"100%\">\n <BarChart data={data} margin={{ top: 4, right: 16, bottom: 4, left: 4 }}>\n <CartesianGrid {...GRID_STYLE} vertical={false} />\n <XAxis\n dataKey=\"label\"\n tickLine={false}\n axisLine={false}\n tick={AXIS_TICK_STYLE}\n tickMargin={8}\n minTickGap={24}\n />\n <YAxis\n tickLine={false}\n axisLine={false}\n tick={AXIS_TICK_STYLE}\n width={44}\n tickFormatter={(v) => formatGrams(Number(v))}\n />\n <Tooltip\n contentStyle={TOOLTIP_STYLE}\n labelStyle={{ color: 'var(--color-text-tertiary)' }}\n formatter={(value, name) => [formatGrams(Number(value)), name as string]}\n />\n <Legend wrapperStyle={{ fontSize: 12, paddingTop: 8 }} />\n <Bar dataKey=\"proteinG\" name=\"Protein\" stackId=\"macros\" fill={MACRO_HUE.protein} />\n <Bar dataKey=\"carbsG\" name=\"Carbs\" stackId=\"macros\" fill={MACRO_HUE.carbs} />\n <Bar dataKey=\"fatG\" name=\"Fat\" stackId=\"macros\" fill={MACRO_HUE.fat} />\n </BarChart>\n </ResponsiveContainer>\n </div>\n </ChartCard>\n );\n}\n\n// ============================================================================\n// Calorie trend\n// ============================================================================\n\nexport function CalorieChart({ daily }: { daily: MybInsightsDay[] }) {\n const data = useChartData(daily);\n\n if (data.length === 0) {\n return (\n <ChartCard\n title=\"Calorie trend\"\n icon={<IconChip icon={<Scale className=\"h-4 w-4\" />} tint={CHART_COLORS.chart1} />}\n >\n <EmptyChart message=\"Log meals to see calorie trends.\" />\n </ChartCard>\n );\n }\n\n return (\n <ChartCard\n title=\"Calorie trend\"\n icon={<IconChip icon={<Scale className=\"h-4 w-4\" />} tint={CHART_COLORS.chart1} />}\n >\n <div className=\"h-[260px] w-full\">\n <ResponsiveContainer width=\"100%\" height=\"100%\">\n <ComposedChart data={data} margin={{ top: 4, right: 16, bottom: 4, left: 4 }}>\n <CartesianGrid {...GRID_STYLE} vertical={false} />\n <XAxis\n dataKey=\"label\"\n tickLine={false}\n axisLine={false}\n tick={AXIS_TICK_STYLE}\n tickMargin={8}\n minTickGap={24}\n />\n <YAxis\n tickLine={false}\n axisLine={false}\n tick={AXIS_TICK_STYLE}\n width={52}\n tickFormatter={(v) => formatKcal(Number(v))}\n />\n <Tooltip\n contentStyle={TOOLTIP_STYLE}\n labelStyle={{ color: 'var(--color-text-tertiary)' }}\n formatter={(value) => [formatKcal(Number(value)), 'Calories']}\n />\n <Area\n type=\"monotone\"\n dataKey=\"calories\"\n name=\"Calories\"\n stroke={CHART_COLORS.chart1}\n strokeWidth={2}\n fill={CHART_COLORS.chart1}\n fillOpacity={0.12}\n dot={false}\n activeDot={{ r: 4, strokeWidth: 2, stroke: 'var(--color-bg-surface)' }}\n />\n </ComposedChart>\n </ResponsiveContainer>\n </div>\n </ChartCard>\n );\n}\n\n// ============================================================================\n// Correlation view\n// ============================================================================\n\ntype CorrelationPair = {\n label: string;\n x: string;\n y: string;\n r: number | null;\n n: number;\n};\n\nfunction pearson(xs: (number | null)[], ys: (number | null)[]): { r: number | null; n: number } {\n const pairs = xs\n .map((x, i) => [x, ys[i]] as const)\n .filter(([x, y]) => x != null && y != null) as [number, number][];\n if (pairs.length < 3) return { r: null, n: pairs.length };\n\n const n = pairs.length;\n const sumX = pairs.reduce((a, [x]) => a + x, 0);\n const sumY = pairs.reduce((a, [, y]) => a + y, 0);\n const sumX2 = pairs.reduce((a, [x]) => a + x * x, 0);\n const sumY2 = pairs.reduce((a, [, y]) => a + y * y, 0);\n const sumXY = pairs.reduce((a, [x, y]) => a + x * y, 0);\n\n const denom = Math.sqrt((n * sumX2 - sumX * sumX) * (n * sumY2 - sumY * sumY));\n if (!Number.isFinite(denom) || denom === 0) return { r: null, n };\n\n return { r: (n * sumXY - sumX * sumY) / denom, n };\n}\n\nfunction useCorrelations(daily: MybInsightsDay[]): CorrelationPair[] {\n return useMemo(() => {\n const readiness = daily.map((d) => d.readiness ?? null);\n const sleep = daily.map((d) => d.sleepHours ?? null);\n const volume = daily.map((d) => d.volumeKg);\n const sessions = daily.map((d) => d.sessions);\n const protein = daily.map((d) => d.proteinG);\n const calories = daily.map((d) => d.calories);\n\n const compute = (\n label: string,\n x: string,\n y: string,\n xs: (number | null)[],\n ys: (number | null)[]\n ) => {\n const { r, n } = pearson(xs, ys);\n return { label, x, y, r, n };\n };\n\n return [\n compute('Sleep → Readiness', 'Sleep', 'Readiness', sleep, readiness),\n compute('Volume → Readiness', 'Volume', 'Readiness', volume, readiness),\n compute('Sessions → Sleep', 'Sessions', 'Sleep', sessions, sleep),\n compute('Protein → Readiness', 'Protein', 'Readiness', protein, readiness),\n compute('Calories → Volume', 'Calories', 'Volume', calories, volume),\n ];\n }, [daily]);\n}\n\nfunction correlationLabel(r: number): string {\n const abs = Math.abs(r);\n if (abs >= 0.7) return 'Strong';\n if (abs >= 0.4) return 'Moderate';\n if (abs >= 0.2) return 'Weak';\n return 'None';\n}\n\nfunction correlationColor(r: number): string {\n if (r > 0) return 'var(--color-status-success-text)';\n if (r < 0) return 'var(--color-status-error-text)';\n return 'var(--color-text-tertiary)';\n}\n\nexport function CorrelationView({ daily }: { daily: MybInsightsDay[] }) {\n const correlations = useCorrelations(daily);\n const available = correlations.filter((c) => c.r != null);\n\n if (available.length === 0) {\n return (\n <GlassCard treatment=\"plain\" className=\"p-6\">\n <div className=\"flex items-center gap-2.5\">\n <IconChip icon={<Moon className=\"h-4 w-4\" />} tint={CHART_COLORS.secondary} />\n <h3 className=\"text-sm font-semibold text-text-primary\">Correlations</h3>\n </div>\n <p className=\"mt-3 text-sm text-text-secondary\">\n Log a few more recovery, training and nutrition days to surface correlations.\n </p>\n </GlassCard>\n );\n }\n\n return (\n <GlassCard treatment=\"plain\" className=\"overflow-hidden p-0\">\n <div className=\"border-b border-border-seam px-5 py-4\">\n <div className=\"flex items-center gap-2.5\">\n <IconChip icon={<Moon className=\"h-4 w-4\" />} tint={CHART_COLORS.secondary} />\n <h3 className=\"text-sm font-semibold text-text-primary\">Correlations</h3>\n </div>\n <p className=\"mt-1 text-xs text-text-secondary\">\n Pearson correlation across the selected range. More data gives more reliable signals.\n </p>\n </div>\n <div className=\"divide-y divide-border-seam/60\">\n {available.map((c) => (\n <div key={c.label} className=\"flex items-center justify-between px-5 py-3\">\n <div>\n <p className=\"text-sm font-medium text-text-primary\">{c.label}</p>\n <p className=\"text-xs text-text-tertiary\">{c.n} days with both metrics</p>\n </div>\n <div className=\"text-right\">\n <p\n className=\"text-sm font-semibold tabular-nums\"\n style={{ color: correlationColor(c.r ?? 0) }}\n >\n {(c.r ?? 0).toFixed(2)}\n </p>\n <p className=\"text-xs text-text-tertiary\">{correlationLabel(c.r ?? 0)}</p>\n </div>\n </div>\n ))}\n </div>\n </GlassCard>\n );\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Insights — unified analytics dashboard for MyBodyShield.
|
|
3
|
+
* @module mybodyshield/pages/insights
|
|
4
|
+
*
|
|
5
|
+
* BOFF-4056: surfaces a 30-day rollup of training volume, sessions,
|
|
6
|
+
* readiness, sleep, nutrition, body weight, and active goals using the
|
|
7
|
+
* `mybInsights` federated query. The backend already aggregates and
|
|
8
|
+
* anonymizes the data; this page only renders what the member is allowed
|
|
9
|
+
* to see.
|
|
10
|
+
*/
|
|
11
|
+
export declare function InsightsPage(): import("react").JSX.Element;
|
|
12
|
+
//# sourceMappingURL=InsightsPage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"InsightsPage.d.ts","sourceRoot":"","sources":["../../../../src/mybodyshield/pages/insights/InsightsPage.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAqEH,wBAAgB,YAAY,gCAsR3B"}
|