@burdenoff/microfe-mybodyshield 2026.706.2 → 2026.713.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/dev/main.d.ts +2 -0
- package/dist/dev/main.d.ts.map +1 -0
- package/dist/generated/global-operations.d.ts +2 -0
- package/dist/generated/global-operations.d.ts.map +1 -0
- package/dist/generated/global-types.d.ts +18199 -0
- package/dist/generated/global-types.d.ts.map +1 -0
- package/dist/generated/wspace-operations.d.ts +2 -0
- package/dist/generated/wspace-operations.d.ts.map +1 -0
- package/dist/generated/wspace-types.d.ts +43046 -0
- package/dist/generated/wspace-types.d.ts.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -1
- package/dist/mybodyshield/MyBodyShieldRoot.d.ts +5 -0
- package/dist/mybodyshield/MyBodyShieldRoot.d.ts.map +1 -0
- package/dist/mybodyshield/MyBodyShieldRoutes.d.ts +4 -0
- package/dist/mybodyshield/MyBodyShieldRoutes.d.ts.map +1 -0
- package/dist/mybodyshield/MyBodyShieldRoutes.js +77 -36
- package/dist/mybodyshield/MyBodyShieldRoutes.js.map +1 -1
- package/dist/mybodyshield/components/DataTable.d.ts +28 -0
- package/dist/mybodyshield/components/DataTable.d.ts.map +1 -0
- package/dist/mybodyshield/components/EmptyState.d.ts +18 -0
- package/dist/mybodyshield/components/EmptyState.d.ts.map +1 -0
- package/dist/mybodyshield/components/EvidenceUpload.d.ts +14 -0
- package/dist/mybodyshield/components/EvidenceUpload.d.ts.map +1 -0
- package/dist/mybodyshield/components/FeatureFlagGuard.d.ts +11 -0
- package/dist/mybodyshield/components/FeatureFlagGuard.d.ts.map +1 -0
- package/dist/mybodyshield/components/FeatureFlagGuard.js +27 -0
- package/dist/mybodyshield/components/FeatureFlagGuard.js.map +1 -0
- package/dist/mybodyshield/components/FileLink.d.ts +15 -0
- package/dist/mybodyshield/components/FileLink.d.ts.map +1 -0
- package/dist/mybodyshield/components/FilterTabs.d.ts +18 -0
- package/dist/mybodyshield/components/FilterTabs.d.ts.map +1 -0
- package/dist/mybodyshield/components/MetricsCard.d.ts +23 -0
- package/dist/mybodyshield/components/MetricsCard.d.ts.map +1 -0
- package/dist/mybodyshield/components/MiniBar.d.ts +16 -0
- package/dist/mybodyshield/components/MiniBar.d.ts.map +1 -0
- package/dist/mybodyshield/components/MybDiscussion.d.ts +18 -0
- package/dist/mybodyshield/components/MybDiscussion.d.ts.map +1 -0
- package/dist/mybodyshield/components/MybDiscussion.js +5 -5
- package/dist/mybodyshield/components/MybTags.d.ts +17 -0
- package/dist/mybodyshield/components/MybTags.d.ts.map +1 -0
- package/dist/mybodyshield/components/PageDisabled.d.ts +9 -0
- package/dist/mybodyshield/components/PageDisabled.d.ts.map +1 -0
- package/dist/mybodyshield/components/PageDisabled.js +17 -0
- package/dist/mybodyshield/components/PageDisabled.js.map +1 -0
- package/dist/mybodyshield/components/PageLayout.d.ts +16 -0
- package/dist/mybodyshield/components/PageLayout.d.ts.map +1 -0
- package/dist/mybodyshield/components/Pagination.d.ts +22 -0
- package/dist/mybodyshield/components/Pagination.d.ts.map +1 -0
- package/dist/mybodyshield/components/StateViews.d.ts +25 -0
- package/dist/mybodyshield/components/StateViews.d.ts.map +1 -0
- package/dist/mybodyshield/components/StateViews.js +2 -1
- package/dist/mybodyshield/components/StateViews.js.map +1 -1
- package/dist/mybodyshield/components/StatusBadge.d.ts +9 -0
- package/dist/mybodyshield/components/StatusBadge.d.ts.map +1 -0
- package/dist/mybodyshield/components/Tabs.d.ts +17 -0
- package/dist/mybodyshield/components/Tabs.d.ts.map +1 -0
- package/dist/mybodyshield/components/UserSearchSelect.d.ts +28 -0
- package/dist/mybodyshield/components/UserSearchSelect.d.ts.map +1 -0
- package/dist/mybodyshield/components/index.d.ts +22 -0
- package/dist/mybodyshield/components/index.d.ts.map +1 -0
- package/dist/mybodyshield/components/index.js +4 -2
- package/dist/mybodyshield/context/MyBodyShieldContext.d.ts +16 -0
- package/dist/mybodyshield/context/MyBodyShieldContext.d.ts.map +1 -0
- package/dist/mybodyshield/context/index.d.ts +6 -0
- package/dist/mybodyshield/context/index.d.ts.map +1 -0
- package/dist/mybodyshield/hooks/index.d.ts +12 -0
- package/dist/mybodyshield/hooks/index.d.ts.map +1 -0
- package/dist/mybodyshield/hooks/index.js +1 -0
- package/dist/mybodyshield/hooks/useMyBodyShieldAnalytics.d.ts +21 -0
- package/dist/mybodyshield/hooks/useMyBodyShieldAnalytics.d.ts.map +1 -0
- package/dist/mybodyshield/hooks/useMyBodyShieldAnalytics.js +35 -0
- package/dist/mybodyshield/hooks/useMyBodyShieldAnalytics.js.map +1 -0
- package/dist/mybodyshield/hooks/useMyBodyShieldApi.d.ts +83 -0
- package/dist/mybodyshield/hooks/useMyBodyShieldApi.d.ts.map +1 -0
- package/dist/mybodyshield/hooks/useMybComments.d.ts +36 -0
- package/dist/mybodyshield/hooks/useMybComments.d.ts.map +1 -0
- package/dist/mybodyshield/hooks/useMybFiles.d.ts +47 -0
- package/dist/mybodyshield/hooks/useMybFiles.d.ts.map +1 -0
- package/dist/mybodyshield/hooks/useMybTags.d.ts +44 -0
- package/dist/mybodyshield/hooks/useMybTags.d.ts.map +1 -0
- package/dist/mybodyshield/hooks/useMybUsers.d.ts +27 -0
- package/dist/mybodyshield/hooks/useMybUsers.d.ts.map +1 -0
- package/dist/mybodyshield/hooks/usePager.d.ts +29 -0
- package/dist/mybodyshield/hooks/usePager.d.ts.map +1 -0
- package/dist/mybodyshield/index.d.ts +9 -0
- package/dist/mybodyshield/index.d.ts.map +1 -0
- package/dist/mybodyshield/pages/DashboardPage.d.ts +10 -0
- package/dist/mybodyshield/pages/DashboardPage.d.ts.map +1 -0
- package/dist/mybodyshield/pages/DashboardPage.js +91 -86
- package/dist/mybodyshield/pages/DashboardPage.js.map +1 -1
- package/dist/mybodyshield/pages/_shared/visuals.d.ts +77 -0
- package/dist/mybodyshield/pages/_shared/visuals.d.ts.map +1 -0
- package/dist/mybodyshield/pages/coaching/CoachingPage.d.ts +11 -0
- package/dist/mybodyshield/pages/coaching/CoachingPage.d.ts.map +1 -0
- package/dist/mybodyshield/pages/coaching/CoachingPage.js +60 -60
- package/dist/mybodyshield/pages/goals/GoalsPage.d.ts +11 -0
- package/dist/mybodyshield/pages/goals/GoalsPage.d.ts.map +1 -0
- package/dist/mybodyshield/pages/goals/GoalsPage.js +125 -125
- package/dist/mybodyshield/pages/goals/GoalsPage.js.map +1 -1
- package/dist/mybodyshield/pages/nutrition/NutritionPage.d.ts +13 -0
- package/dist/mybodyshield/pages/nutrition/NutritionPage.d.ts.map +1 -0
- package/dist/mybodyshield/pages/nutrition/NutritionPage.js +129 -129
- package/dist/mybodyshield/pages/nutrition/NutritionPage.js.map +1 -1
- package/dist/mybodyshield/pages/programs/ProgramDetailPage.d.ts +10 -0
- package/dist/mybodyshield/pages/programs/ProgramDetailPage.d.ts.map +1 -0
- package/dist/mybodyshield/pages/programs/ProgramDetailPage.js +218 -191
- package/dist/mybodyshield/pages/programs/ProgramDetailPage.js.map +1 -1
- package/dist/mybodyshield/pages/programs/ProgramsPage.d.ts +11 -0
- package/dist/mybodyshield/pages/programs/ProgramsPage.d.ts.map +1 -0
- package/dist/mybodyshield/pages/programs/ProgramsPage.js +197 -193
- package/dist/mybodyshield/pages/programs/ProgramsPage.js.map +1 -1
- package/dist/mybodyshield/pages/progress/ProgressPage.d.ts +13 -0
- package/dist/mybodyshield/pages/progress/ProgressPage.d.ts.map +1 -0
- package/dist/mybodyshield/pages/progress/ProgressPage.js +143 -143
- package/dist/mybodyshield/pages/progress/ProgressPage.js.map +1 -1
- package/dist/mybodyshield/pages/recovery/RecoveryPage.d.ts +11 -0
- package/dist/mybodyshield/pages/recovery/RecoveryPage.d.ts.map +1 -0
- package/dist/mybodyshield/pages/recovery/RecoveryPage.js +60 -60
- package/dist/mybodyshield/pages/recovery/RecoveryPage.js.map +1 -1
- package/dist/mybodyshield/pages/train/SessionDetailPage.d.ts +11 -0
- package/dist/mybodyshield/pages/train/SessionDetailPage.d.ts.map +1 -0
- package/dist/mybodyshield/pages/train/SessionDetailPage.js +184 -181
- package/dist/mybodyshield/pages/train/SessionDetailPage.js.map +1 -1
- package/dist/mybodyshield/pages/train/SessionsPage.d.ts +11 -0
- package/dist/mybodyshield/pages/train/SessionsPage.d.ts.map +1 -0
- package/dist/mybodyshield/pages/train/SessionsPage.js +119 -115
- package/dist/mybodyshield/pages/train/SessionsPage.js.map +1 -1
- package/dist/mybodyshield/services/createSandboxAssistantTransport.d.ts +24 -0
- package/dist/mybodyshield/services/createSandboxAssistantTransport.d.ts.map +1 -0
- package/dist/mybodyshield/services/createSandboxAssistantTransport.js +14 -0
- package/dist/mybodyshield/services/createSandboxAssistantTransport.js.map +1 -0
- package/dist/mybodyshield/types/assistant.d.ts +55 -0
- package/dist/mybodyshield/types/assistant.d.ts.map +1 -0
- package/dist/mybodyshield/types/index.d.ts +419 -0
- package/dist/mybodyshield/types/index.d.ts.map +1 -0
- package/dist/mybodyshield/utils/cn.d.ts +4 -0
- package/dist/mybodyshield/utils/cn.d.ts.map +1 -0
- package/dist/mybodyshield/utils/formatters.d.ts +32 -0
- package/dist/mybodyshield/utils/formatters.d.ts.map +1 -0
- package/dist/mybodyshield/utils/i18n.d.ts +6 -0
- package/dist/mybodyshield/utils/i18n.d.ts.map +1 -0
- package/dist/mybodyshield/utils/index.d.ts +9 -0
- package/dist/mybodyshield/utils/index.d.ts.map +1 -0
- package/dist/mybodyshield/utils/navigation.d.ts +14 -0
- package/dist/mybodyshield/utils/navigation.d.ts.map +1 -0
- package/dist/mybodyshield/utils/pageContext.d.ts +28 -0
- package/dist/mybodyshield/utils/pageContext.d.ts.map +1 -0
- package/dist/mybodyshield/utils/pageContext.js +62 -0
- package/dist/mybodyshield/utils/pageContext.js.map +1 -0
- package/package.json +8 -2
|
@@ -1,25 +1,26 @@
|
|
|
1
1
|
import { useMyBodyShield as e } from "../../context/MyBodyShieldContext.js";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import { IconChip as l, Meter as ne } from "../_shared/visuals.js";
|
|
2
|
+
import { PageLayout as t } from "../../components/PageLayout.js";
|
|
3
|
+
import { formatKcal as n, formatNumber as ee, formatRelativeTime as te } from "../../utils/formatters.js";
|
|
4
|
+
import { StatusBadge as ne } from "../../components/StatusBadge.js";
|
|
5
|
+
import { EmptyState as r } from "../../components/EmptyState.js";
|
|
6
|
+
import { ErrorView as i, LoadingView as a } from "../../components/StateViews.js";
|
|
7
|
+
import { FilterTabs as o } from "../../components/FilterTabs.js";
|
|
8
|
+
import { Pagination as s } from "../../components/Pagination.js";
|
|
10
9
|
import "../../context/index.js";
|
|
11
|
-
import { useMybPrograms as
|
|
12
|
-
import { useOffsetPager as
|
|
10
|
+
import { useMybPrograms as c, useMybSessions as l, useMybStartSession as re } from "../../hooks/useMyBodyShieldApi.js";
|
|
11
|
+
import { useOffsetPager as ie } from "../../hooks/usePager.js";
|
|
12
|
+
import { useMyBodyShieldAnalytics as u } from "../../hooks/useMyBodyShieldAnalytics.js";
|
|
13
13
|
import "../../hooks/index.js";
|
|
14
|
-
import { FilterTabs as p } from "../../components/FilterTabs.js";
|
|
15
|
-
import { Pagination as ie } from "../../components/Pagination.js";
|
|
16
14
|
import "../../components/index.js";
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
19
|
-
import {
|
|
20
|
-
import {
|
|
15
|
+
import { motion as d } from "../../../node_modules/framer-motion/dist/es/render/components/motion/proxy.js";
|
|
16
|
+
import { useReducedMotion as ae } from "../../../node_modules/framer-motion/dist/es/utils/reduced-motion/use-reduced-motion.js";
|
|
17
|
+
import { IconChip as f, Meter as oe } from "../_shared/visuals.js";
|
|
18
|
+
import { useState as p } from "react";
|
|
19
|
+
import { CalendarClock as m, CheckCircle2 as h, Flame as g, Gauge as se, ListChecks as ce, PlayCircle as _, Plus as v, SkipForward as y } from "lucide-react";
|
|
20
|
+
import { Fragment as b, jsx as x, jsxs as S } from "react/jsx-runtime";
|
|
21
|
+
import { Button as C, GlassCard as le, Input as ue, Label as w, ResponsiveDialog as T, ResponsiveDialogContent as E, ResponsiveDialogDescription as D, ResponsiveDialogFooter as O, ResponsiveDialogHeader as k, ResponsiveDialogTitle as A, Select as j, SelectContent as M, SelectItem as N, SelectTrigger as P, SelectValue as F, Textarea as I } from "@burdenoff/fe-libs/ui";
|
|
21
22
|
//#region src/mybodyshield/pages/train/SessionsPage.tsx
|
|
22
|
-
var
|
|
23
|
+
var L = [
|
|
23
24
|
{
|
|
24
25
|
value: "ALL",
|
|
25
26
|
label: "All"
|
|
@@ -36,41 +37,41 @@ var R = [
|
|
|
36
37
|
value: "COMPLETED",
|
|
37
38
|
label: "Completed"
|
|
38
39
|
}
|
|
39
|
-
],
|
|
40
|
+
], R = 24, z = [
|
|
40
41
|
.16,
|
|
41
42
|
1,
|
|
42
43
|
.3,
|
|
43
44
|
1
|
|
44
|
-
],
|
|
45
|
-
programId:
|
|
45
|
+
], B = "__none__", V = {
|
|
46
|
+
programId: B,
|
|
46
47
|
scheduledFor: "",
|
|
47
48
|
notes: ""
|
|
48
49
|
};
|
|
49
|
-
function
|
|
50
|
+
function H(e) {
|
|
50
51
|
switch (e.status) {
|
|
51
52
|
case "IN_PROGRESS": return {
|
|
52
|
-
icon:
|
|
53
|
+
icon: _,
|
|
53
54
|
tint: "var(--color-accent-teal)",
|
|
54
55
|
title: "Live workout in progress",
|
|
55
56
|
stampLabel: "Started",
|
|
56
57
|
stamp: e.startedAt ?? e.createdAt
|
|
57
58
|
};
|
|
58
59
|
case "COMPLETED": return {
|
|
59
|
-
icon:
|
|
60
|
+
icon: h,
|
|
60
61
|
tint: "var(--color-status-success-text)",
|
|
61
|
-
title: e.caloriesBurned > 0 ? `${
|
|
62
|
+
title: e.caloriesBurned > 0 ? `${n(e.caloriesBurned)} burned` : "Workout completed",
|
|
62
63
|
stampLabel: "Completed",
|
|
63
64
|
stamp: e.completedAt ?? e.startedAt ?? e.createdAt
|
|
64
65
|
};
|
|
65
66
|
case "SKIPPED": return {
|
|
66
|
-
icon:
|
|
67
|
+
icon: y,
|
|
67
68
|
tint: "var(--color-text-secondary)",
|
|
68
69
|
title: "Skipped session",
|
|
69
70
|
stampLabel: "Scheduled",
|
|
70
71
|
stamp: e.scheduledFor ?? e.createdAt
|
|
71
72
|
};
|
|
72
73
|
default: return {
|
|
73
|
-
icon:
|
|
74
|
+
icon: m,
|
|
74
75
|
tint: "var(--color-accent-primary)",
|
|
75
76
|
title: "Scheduled session",
|
|
76
77
|
stampLabel: "Scheduled",
|
|
@@ -78,58 +79,58 @@ function U(e) {
|
|
|
78
79
|
};
|
|
79
80
|
}
|
|
80
81
|
}
|
|
81
|
-
function
|
|
82
|
-
let { navigateTo: h } = e(),
|
|
83
|
-
status:
|
|
82
|
+
function U() {
|
|
83
|
+
let { navigateTo: m, workspaceId: h } = e(), _ = ae(), y = u(), [U, de] = p("ALL"), W = ie(R), G = l({
|
|
84
|
+
status: U === "ALL" ? void 0 : U,
|
|
84
85
|
pagination: {
|
|
85
|
-
limit:
|
|
86
|
+
limit: R,
|
|
86
87
|
offset: W.offset
|
|
87
88
|
}
|
|
88
|
-
}),
|
|
89
|
+
}), fe = c({
|
|
89
90
|
status: "ACTIVE",
|
|
90
91
|
pagination: { limit: 50 }
|
|
91
|
-
}).data?.items ?? [], K =
|
|
92
|
-
|
|
92
|
+
}).data?.items ?? [], K = re(), [pe, q] = p(!1), [J, Y] = p(V), [X, Z] = p(null), Q = G.data?.items ?? [], me = G.data?.pageInfo?.hasMore ?? !1, he = (e) => {
|
|
93
|
+
de(e), W.reset();
|
|
93
94
|
}, $ = () => {
|
|
94
|
-
Y(
|
|
95
|
+
Y(V), Z(null), q(!0), y.emitIntent("session", "create", { workspaceId: h });
|
|
95
96
|
};
|
|
96
|
-
return /* @__PURE__ */
|
|
97
|
+
return /* @__PURE__ */ S(t, {
|
|
97
98
|
title: "Train",
|
|
98
99
|
description: "Live workout sessions — start, log sets, and complete.",
|
|
99
|
-
actions: /* @__PURE__ */ C
|
|
100
|
+
actions: /* @__PURE__ */ S(C, {
|
|
100
101
|
onClick: $,
|
|
101
|
-
children: [/* @__PURE__ */
|
|
102
|
+
children: [/* @__PURE__ */ x(v, { className: "h-4 w-4" }), " Start session"]
|
|
102
103
|
}),
|
|
103
104
|
children: [
|
|
104
|
-
/* @__PURE__ */
|
|
105
|
+
/* @__PURE__ */ x("div", {
|
|
105
106
|
className: "mb-6",
|
|
106
|
-
children: /* @__PURE__ */
|
|
107
|
-
options:
|
|
108
|
-
value:
|
|
109
|
-
onChange:
|
|
107
|
+
children: /* @__PURE__ */ x(o, {
|
|
108
|
+
options: L,
|
|
109
|
+
value: U,
|
|
110
|
+
onChange: he
|
|
110
111
|
})
|
|
111
112
|
}),
|
|
112
|
-
G.isLoading ? /* @__PURE__ */
|
|
113
|
+
G.isLoading ? /* @__PURE__ */ x(a, { label: "Loading your sessions…" }) : G.isError ? /* @__PURE__ */ x(i, {
|
|
113
114
|
message: "We couldn't load your workout sessions.",
|
|
114
115
|
onRetry: () => G.refetch()
|
|
115
|
-
}) : Q.length === 0 ? /* @__PURE__ */
|
|
116
|
+
}) : Q.length === 0 ? /* @__PURE__ */ x(r, {
|
|
116
117
|
illustration: "empty-data",
|
|
117
|
-
icon: /* @__PURE__ */
|
|
118
|
-
title:
|
|
119
|
-
description:
|
|
120
|
-
action: /* @__PURE__ */ C
|
|
118
|
+
icon: /* @__PURE__ */ x(g, { className: "h-10 w-10" }),
|
|
119
|
+
title: U === "ALL" ? "No sessions yet" : "No sessions in this state",
|
|
120
|
+
description: U === "ALL" ? "Start a workout session to begin logging sets and tracking your training." : "Try a different filter, or start a new workout session.",
|
|
121
|
+
action: /* @__PURE__ */ S(C, {
|
|
121
122
|
onClick: $,
|
|
122
|
-
children: [/* @__PURE__ */
|
|
123
|
+
children: [/* @__PURE__ */ x(v, { className: "h-4 w-4" }), " Start session"]
|
|
123
124
|
})
|
|
124
|
-
}) : /* @__PURE__ */
|
|
125
|
+
}) : /* @__PURE__ */ S(b, { children: [/* @__PURE__ */ x("div", {
|
|
125
126
|
className: "grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3",
|
|
126
|
-
children: Q.map((e,
|
|
127
|
-
let r =
|
|
128
|
-
return /* @__PURE__ */
|
|
127
|
+
children: Q.map((e, t) => {
|
|
128
|
+
let r = H(e), i = r.icon, a = e.status === "IN_PROGRESS", o = e.sets?.length, s = e.perceivedExertion;
|
|
129
|
+
return /* @__PURE__ */ x(d.button, {
|
|
129
130
|
type: "button",
|
|
130
|
-
onClick: () =>
|
|
131
|
+
onClick: () => m(`train/${e.id}`),
|
|
131
132
|
className: "block w-full text-left",
|
|
132
|
-
initial:
|
|
133
|
+
initial: _ ? !1 : {
|
|
133
134
|
opacity: 0,
|
|
134
135
|
y: 12
|
|
135
136
|
},
|
|
@@ -137,77 +138,77 @@ function W() {
|
|
|
137
138
|
opacity: 1,
|
|
138
139
|
y: 0
|
|
139
140
|
},
|
|
140
|
-
transition:
|
|
141
|
+
transition: _ ? { duration: 0 } : {
|
|
141
142
|
duration: .35,
|
|
142
|
-
delay: Math.min(
|
|
143
|
-
ease:
|
|
143
|
+
delay: Math.min(t, 8) * .05,
|
|
144
|
+
ease: z
|
|
144
145
|
},
|
|
145
|
-
whileHover:
|
|
146
|
-
whileTap:
|
|
147
|
-
children: /* @__PURE__ */
|
|
146
|
+
whileHover: _ ? void 0 : { y: -3 },
|
|
147
|
+
whileTap: _ ? void 0 : { scale: .995 },
|
|
148
|
+
children: /* @__PURE__ */ S(le, {
|
|
148
149
|
treatment: "plain",
|
|
149
|
-
glow:
|
|
150
|
-
className: "h-full p-4 transition-shadow hover:shadow-float" + (
|
|
150
|
+
glow: a,
|
|
151
|
+
className: "h-full p-4 transition-shadow hover:shadow-float" + (a ? " ring-1 ring-inset ring-[color-mix(in_oklab,var(--color-action-primary-bg)_45%,transparent)]" : ""),
|
|
151
152
|
children: [
|
|
152
|
-
/* @__PURE__ */
|
|
153
|
+
/* @__PURE__ */ S("div", {
|
|
153
154
|
className: "flex items-center justify-between gap-2",
|
|
154
|
-
children: [/* @__PURE__ */
|
|
155
|
-
icon: /* @__PURE__ */
|
|
155
|
+
children: [/* @__PURE__ */ x(f, {
|
|
156
|
+
icon: /* @__PURE__ */ x(i, { className: "h-4 w-4" }),
|
|
156
157
|
tint: r.tint
|
|
157
|
-
}), /* @__PURE__ */
|
|
158
|
+
}), /* @__PURE__ */ x(ne, { status: e.status })]
|
|
158
159
|
}),
|
|
159
|
-
/* @__PURE__ */
|
|
160
|
+
/* @__PURE__ */ x("p", {
|
|
160
161
|
className: "mt-3 text-base font-semibold text-text-primary",
|
|
161
162
|
children: r.title
|
|
162
163
|
}),
|
|
163
|
-
/* @__PURE__ */
|
|
164
|
+
/* @__PURE__ */ S("p", {
|
|
164
165
|
className: "mt-0.5 text-xs text-text-tertiary",
|
|
165
166
|
children: [
|
|
166
167
|
r.stampLabel,
|
|
167
168
|
" ",
|
|
168
|
-
|
|
169
|
+
te(r.stamp)
|
|
169
170
|
]
|
|
170
171
|
}),
|
|
171
|
-
/* @__PURE__ */
|
|
172
|
+
/* @__PURE__ */ S("div", {
|
|
172
173
|
className: "mt-3 flex flex-wrap items-center gap-x-4 gap-y-1 text-xs text-text-secondary",
|
|
173
174
|
children: [
|
|
174
|
-
e.caloriesBurned > 0 && /* @__PURE__ */
|
|
175
|
+
e.caloriesBurned > 0 && /* @__PURE__ */ S("span", {
|
|
175
176
|
className: "inline-flex items-center gap-1 tabular-nums",
|
|
176
|
-
children: [/* @__PURE__ */
|
|
177
|
+
children: [/* @__PURE__ */ x(g, { className: "h-3.5 w-3.5 text-status-warning-text" }), n(e.caloriesBurned)]
|
|
177
178
|
}),
|
|
178
|
-
|
|
179
|
+
s != null && /* @__PURE__ */ S("span", {
|
|
179
180
|
className: "inline-flex items-center gap-1 tabular-nums",
|
|
180
181
|
children: [
|
|
181
|
-
/* @__PURE__ */
|
|
182
|
+
/* @__PURE__ */ x(se, { className: "h-3.5 w-3.5 text-accent" }),
|
|
182
183
|
"RPE ",
|
|
183
|
-
|
|
184
|
+
s,
|
|
184
185
|
"/10"
|
|
185
186
|
]
|
|
186
187
|
}),
|
|
187
|
-
|
|
188
|
+
o != null && o > 0 && /* @__PURE__ */ S("span", {
|
|
188
189
|
className: "inline-flex items-center gap-1 tabular-nums",
|
|
189
190
|
children: [
|
|
190
|
-
/* @__PURE__ */
|
|
191
|
-
|
|
191
|
+
/* @__PURE__ */ x(ce, { className: "h-3.5 w-3.5 text-accent" }),
|
|
192
|
+
ee(o),
|
|
192
193
|
" sets"
|
|
193
194
|
]
|
|
194
195
|
})
|
|
195
196
|
]
|
|
196
197
|
}),
|
|
197
|
-
|
|
198
|
+
s != null && /* @__PURE__ */ S("div", {
|
|
198
199
|
className: "mt-3",
|
|
199
|
-
children: [/* @__PURE__ */
|
|
200
|
+
children: [/* @__PURE__ */ S("div", {
|
|
200
201
|
className: "mb-1 flex items-center justify-between text-[11px] text-text-tertiary",
|
|
201
|
-
children: [/* @__PURE__ */
|
|
202
|
+
children: [/* @__PURE__ */ x("span", { children: "RPE intensity" }), /* @__PURE__ */ S("span", {
|
|
202
203
|
className: "tabular-nums",
|
|
203
|
-
children: [
|
|
204
|
+
children: [s, "/10"]
|
|
204
205
|
})]
|
|
205
|
-
}), /* @__PURE__ */
|
|
206
|
-
value:
|
|
206
|
+
}), /* @__PURE__ */ x(oe, {
|
|
207
|
+
value: s * 10,
|
|
207
208
|
height: 6
|
|
208
209
|
})]
|
|
209
210
|
}),
|
|
210
|
-
|
|
211
|
+
a && /* @__PURE__ */ x("p", {
|
|
211
212
|
className: "mt-3 inline-flex items-center gap-1 text-xs font-semibold text-action-primary-bg",
|
|
212
213
|
children: "In progress — Resume →"
|
|
213
214
|
})
|
|
@@ -215,52 +216,52 @@ function W() {
|
|
|
215
216
|
})
|
|
216
217
|
}, e.id);
|
|
217
218
|
})
|
|
218
|
-
}), /* @__PURE__ */
|
|
219
|
+
}), /* @__PURE__ */ x(s, {
|
|
219
220
|
page: W.page,
|
|
220
|
-
pageSize:
|
|
221
|
+
pageSize: R,
|
|
221
222
|
itemCount: Q.length,
|
|
222
|
-
hasMore:
|
|
223
|
+
hasMore: me,
|
|
223
224
|
onPageChange: (e) => W.onPageChange(e)
|
|
224
225
|
})] }),
|
|
225
|
-
/* @__PURE__ */
|
|
226
|
-
open:
|
|
226
|
+
/* @__PURE__ */ x(T, {
|
|
227
|
+
open: pe,
|
|
227
228
|
onOpenChange: q,
|
|
228
|
-
children: /* @__PURE__ */
|
|
229
|
-
/* @__PURE__ */
|
|
230
|
-
/* @__PURE__ */
|
|
229
|
+
children: /* @__PURE__ */ S(E, { children: [
|
|
230
|
+
/* @__PURE__ */ S(k, { children: [/* @__PURE__ */ x(A, { children: "Start a workout session" }), /* @__PURE__ */ x(D, { children: "Begin a live session now, or schedule one for later. Attach an active program to keep your training organised." })] }),
|
|
231
|
+
/* @__PURE__ */ S("div", {
|
|
231
232
|
className: "space-y-4 px-1",
|
|
232
233
|
children: [
|
|
233
|
-
/* @__PURE__ */
|
|
234
|
+
/* @__PURE__ */ S("div", {
|
|
234
235
|
className: "space-y-1.5",
|
|
235
|
-
children: [/* @__PURE__ */
|
|
236
|
+
children: [/* @__PURE__ */ x(w, {
|
|
236
237
|
htmlFor: "myb-start-program",
|
|
237
238
|
children: "Program (optional)"
|
|
238
|
-
}), /* @__PURE__ */
|
|
239
|
+
}), /* @__PURE__ */ S(j, {
|
|
239
240
|
value: J.programId,
|
|
240
241
|
onValueChange: (e) => Y((t) => ({
|
|
241
242
|
...t,
|
|
242
243
|
programId: e
|
|
243
244
|
})),
|
|
244
|
-
children: [/* @__PURE__ */
|
|
245
|
+
children: [/* @__PURE__ */ x(P, {
|
|
245
246
|
id: "myb-start-program",
|
|
246
|
-
children: /* @__PURE__ */
|
|
247
|
-
}), /* @__PURE__ */
|
|
248
|
-
value:
|
|
247
|
+
children: /* @__PURE__ */ x(F, { placeholder: "No program" })
|
|
248
|
+
}), /* @__PURE__ */ S(M, { children: [/* @__PURE__ */ x(N, {
|
|
249
|
+
value: B,
|
|
249
250
|
children: "No program"
|
|
250
|
-
}),
|
|
251
|
+
}), fe.map((e) => /* @__PURE__ */ x(N, {
|
|
251
252
|
value: e.id,
|
|
252
253
|
children: e.name
|
|
253
254
|
}, e.id))] })]
|
|
254
255
|
})]
|
|
255
256
|
}),
|
|
256
|
-
/* @__PURE__ */
|
|
257
|
+
/* @__PURE__ */ S("div", {
|
|
257
258
|
className: "space-y-1.5",
|
|
258
259
|
children: [
|
|
259
|
-
/* @__PURE__ */
|
|
260
|
+
/* @__PURE__ */ x(w, {
|
|
260
261
|
htmlFor: "myb-start-scheduled",
|
|
261
262
|
children: "Scheduled for (optional)"
|
|
262
263
|
}),
|
|
263
|
-
/* @__PURE__ */
|
|
264
|
+
/* @__PURE__ */ x(ue, {
|
|
264
265
|
id: "myb-start-scheduled",
|
|
265
266
|
type: "datetime-local",
|
|
266
267
|
value: J.scheduledFor,
|
|
@@ -269,18 +270,18 @@ function W() {
|
|
|
269
270
|
scheduledFor: e.target.value
|
|
270
271
|
}))
|
|
271
272
|
}),
|
|
272
|
-
/* @__PURE__ */
|
|
273
|
+
/* @__PURE__ */ x("p", {
|
|
273
274
|
className: "text-xs text-text-tertiary",
|
|
274
275
|
children: "Leave empty to start a live session right now."
|
|
275
276
|
})
|
|
276
277
|
]
|
|
277
278
|
}),
|
|
278
|
-
/* @__PURE__ */
|
|
279
|
+
/* @__PURE__ */ S("div", {
|
|
279
280
|
className: "space-y-1.5",
|
|
280
|
-
children: [/* @__PURE__ */
|
|
281
|
+
children: [/* @__PURE__ */ x(w, {
|
|
281
282
|
htmlFor: "myb-start-notes",
|
|
282
283
|
children: "Notes (optional)"
|
|
283
|
-
}), /* @__PURE__ */
|
|
284
|
+
}), /* @__PURE__ */ x(I, {
|
|
284
285
|
id: "myb-start-notes",
|
|
285
286
|
rows: 3,
|
|
286
287
|
value: J.notes,
|
|
@@ -291,27 +292,30 @@ function W() {
|
|
|
291
292
|
placeholder: "Focus, target lifts, how you're feeling…"
|
|
292
293
|
})]
|
|
293
294
|
}),
|
|
294
|
-
X && /* @__PURE__ */
|
|
295
|
+
X && /* @__PURE__ */ x("p", {
|
|
295
296
|
className: "text-sm text-status-error-text",
|
|
296
297
|
children: X
|
|
297
298
|
})
|
|
298
299
|
]
|
|
299
300
|
}),
|
|
300
|
-
/* @__PURE__ */
|
|
301
|
+
/* @__PURE__ */ S(O, { children: [/* @__PURE__ */ x(C, {
|
|
301
302
|
variant: "outline",
|
|
302
303
|
onClick: () => q(!1),
|
|
303
304
|
disabled: K.isPending,
|
|
304
305
|
children: "Cancel"
|
|
305
|
-
}), /* @__PURE__ */
|
|
306
|
+
}), /* @__PURE__ */ x(C, {
|
|
306
307
|
onClick: async () => {
|
|
307
308
|
Z(null);
|
|
308
309
|
try {
|
|
309
310
|
let e = await K.mutateAsync({
|
|
310
|
-
programId: J.programId ===
|
|
311
|
+
programId: J.programId === B ? void 0 : J.programId,
|
|
311
312
|
scheduledFor: J.scheduledFor ? new Date(J.scheduledFor).toISOString() : void 0,
|
|
312
313
|
notes: J.notes.trim() ? J.notes.trim() : void 0
|
|
313
314
|
});
|
|
314
|
-
|
|
315
|
+
y.emitFact("session", "started", {
|
|
316
|
+
workspaceId: h,
|
|
317
|
+
entityId: e.id
|
|
318
|
+
}), q(!1), m(`train/${e.id}`);
|
|
315
319
|
} catch (e) {
|
|
316
320
|
Z(e instanceof Error ? e.message : "Failed to start the session.");
|
|
317
321
|
}
|
|
@@ -325,6 +329,6 @@ function W() {
|
|
|
325
329
|
});
|
|
326
330
|
}
|
|
327
331
|
//#endregion
|
|
328
|
-
export {
|
|
332
|
+
export { U as SessionsPage };
|
|
329
333
|
|
|
330
334
|
//# sourceMappingURL=SessionsPage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SessionsPage.js","names":[],"sources":["../../../../src/mybodyshield/pages/train/SessionsPage.tsx"],"sourcesContent":["/**\n * Train — live workout sessions.\n * @module mybodyshield/pages/train\n *\n * Lists the member's workout sessions (real `mybListSessions`) filtered by\n * status, and lets you start a new live session (optionally attached to an\n * active program). In-progress sessions are highlighted with an accent ring and\n * a \"Resume\" affordance. No mock data — everything comes from the myb* hooks.\n */\n\nimport { useState } from 'react';\nimport { motion, useReducedMotion } from 'framer-motion';\nimport {\n Plus,\n Flame,\n CheckCircle2,\n CalendarClock,\n PlayCircle,\n SkipForward,\n Gauge,\n ListChecks,\n} from 'lucide-react';\nimport {\n Button,\n GlassCard,\n Label,\n Textarea,\n Input,\n Select,\n SelectTrigger,\n SelectContent,\n SelectItem,\n SelectValue,\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n ResponsiveDialogDescription,\n ResponsiveDialogFooter,\n} from '@burdenoff/fe-libs/ui';\nimport {\n PageLayout,\n StatusBadge,\n FilterTabs,\n Pagination,\n LoadingView,\n ErrorView,\n EmptyState,\n type FilterOption,\n} from '../../components';\nimport { IconChip, Meter } from '../_shared/visuals';\nimport { useMyBodyShield } from '../../context';\nimport { useMybSessions, useMybStartSession, useMybPrograms, useOffsetPager } from '../../hooks';\nimport { formatRelativeTime, formatKcal, formatNumber } from '../../utils/formatters';\nimport type { MybSessionStatus, MybWorkoutSession } from '../../types';\n\ntype SessionFilterValue = MybSessionStatus | 'ALL';\n\n// Only statuses a control can actually reach are offered as filters: start →\n// IN_PROGRESS / SCHEDULED, complete → COMPLETED. Nothing moves a session to\n// SKIPPED, so that tab would always be empty. sessionMeta still handles the\n// SKIPPED case for display safety.\nconst STATUS_TABS: FilterOption<SessionFilterValue>[] = [\n { value: 'ALL', label: 'All' },\n { value: 'SCHEDULED', label: 'Scheduled' },\n { value: 'IN_PROGRESS', label: 'Live' },\n { value: 'COMPLETED', label: 'Completed' },\n];\n\nconst PAGE_SIZE = 24;\n\n/** Shared entrance easing — a soft, decelerating cubic-bezier. */\nconst MOTION_EASE: [number, number, number, number] = [0.16, 1, 0.3, 1];\n\n/** Sentinel for the optional program picker (Radix Select forbids an empty value). */\nconst NO_PROGRAM = '__none__';\n\ninterface StartForm {\n programId: string;\n scheduledFor: string;\n notes: string;\n}\n\nconst EMPTY_START_FORM: StartForm = { programId: NO_PROGRAM, scheduledFor: '', notes: '' };\n\ninterface SessionMeta {\n icon: typeof Flame;\n tint: string;\n title: string;\n stampLabel: string;\n stamp?: string | null;\n}\n\nfunction sessionMeta(s: MybWorkoutSession): SessionMeta {\n switch (s.status) {\n case 'IN_PROGRESS':\n return {\n icon: PlayCircle,\n tint: 'var(--color-accent-teal)',\n title: 'Live workout in progress',\n stampLabel: 'Started',\n stamp: s.startedAt ?? s.createdAt,\n };\n case 'COMPLETED':\n return {\n icon: CheckCircle2,\n tint: 'var(--color-status-success-text)',\n title:\n s.caloriesBurned > 0 ? `${formatKcal(s.caloriesBurned)} burned` : 'Workout completed',\n stampLabel: 'Completed',\n stamp: s.completedAt ?? s.startedAt ?? s.createdAt,\n };\n case 'SKIPPED':\n return {\n icon: SkipForward,\n tint: 'var(--color-text-secondary)',\n title: 'Skipped session',\n stampLabel: 'Scheduled',\n stamp: s.scheduledFor ?? s.createdAt,\n };\n case 'SCHEDULED':\n default:\n return {\n icon: CalendarClock,\n tint: 'var(--color-accent-primary)',\n title: 'Scheduled session',\n stampLabel: 'Scheduled',\n stamp: s.scheduledFor ?? s.createdAt,\n };\n }\n}\n\nexport function SessionsPage() {\n const { navigateTo } = useMyBodyShield();\n const reduce = useReducedMotion();\n\n const [filter, setFilter] = useState<SessionFilterValue>('ALL');\n const pager = useOffsetPager(PAGE_SIZE);\n\n const sessions = useMybSessions({\n status: filter === 'ALL' ? undefined : filter,\n pagination: { limit: PAGE_SIZE, offset: pager.offset },\n });\n\n const programs = useMybPrograms({ status: 'ACTIVE', pagination: { limit: 50 } });\n const activePrograms = programs.data?.items ?? [];\n\n const startSession = useMybStartSession();\n\n const [startOpen, setStartOpen] = useState(false);\n const [form, setForm] = useState<StartForm>(EMPTY_START_FORM);\n const [formError, setFormError] = useState<string | null>(null);\n\n const rows = sessions.data?.items ?? [];\n const hasMore = sessions.data?.pageInfo?.hasMore ?? false;\n\n const onFilterChange = (value: SessionFilterValue) => {\n setFilter(value);\n pager.reset();\n };\n\n const openStart = () => {\n setForm(EMPTY_START_FORM);\n setFormError(null);\n setStartOpen(true);\n };\n\n const submitStart = async () => {\n setFormError(null);\n try {\n const created = await startSession.mutateAsync({\n programId: form.programId === NO_PROGRAM ? undefined : form.programId,\n scheduledFor: form.scheduledFor ? new Date(form.scheduledFor).toISOString() : undefined,\n notes: form.notes.trim() ? form.notes.trim() : undefined,\n });\n setStartOpen(false);\n navigateTo(`train/${created.id}`);\n } catch (e) {\n setFormError(e instanceof Error ? e.message : 'Failed to start the session.');\n }\n };\n\n return (\n <PageLayout\n title=\"Train\"\n description=\"Live workout sessions — start, log sets, and complete.\"\n actions={\n <Button onClick={openStart}>\n <Plus className=\"h-4 w-4\" /> Start session\n </Button>\n }\n >\n <div className=\"mb-6\">\n <FilterTabs options={STATUS_TABS} value={filter} onChange={onFilterChange} />\n </div>\n\n {sessions.isLoading ? (\n <LoadingView label=\"Loading your sessions…\" />\n ) : sessions.isError ? (\n <ErrorView\n message=\"We couldn't load your workout sessions.\"\n onRetry={() => sessions.refetch()}\n />\n ) : rows.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n icon={<Flame className=\"h-10 w-10\" />}\n title={filter === 'ALL' ? 'No sessions yet' : 'No sessions in this state'}\n description={\n filter === 'ALL'\n ? 'Start a workout session to begin logging sets and tracking your training.'\n : 'Try a different filter, or start a new workout session.'\n }\n action={\n <Button onClick={openStart}>\n <Plus className=\"h-4 w-4\" /> Start session\n </Button>\n }\n />\n ) : (\n <>\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n {rows.map((s, i) => {\n const meta = sessionMeta(s);\n const Icon = meta.icon;\n const isLive = s.status === 'IN_PROGRESS';\n const setCount = s.sets?.length;\n const rpe = s.perceivedExertion;\n return (\n <motion.button\n key={s.id}\n type=\"button\"\n onClick={() => navigateTo(`train/${s.id}`)}\n className=\"block w-full text-left\"\n initial={reduce ? false : { opacity: 0, y: 12 }}\n animate={{ opacity: 1, y: 0 }}\n transition={\n reduce\n ? { duration: 0 }\n : { duration: 0.35, delay: Math.min(i, 8) * 0.05, ease: MOTION_EASE }\n }\n whileHover={reduce ? undefined : { y: -3 }}\n whileTap={reduce ? undefined : { scale: 0.995 }}\n >\n <GlassCard\n treatment=\"plain\"\n glow={isLive}\n className={\n 'h-full p-4 transition-shadow hover:shadow-float' +\n (isLive\n ? ' ring-1 ring-inset ring-[color-mix(in_oklab,var(--color-action-primary-bg)_45%,transparent)]'\n : '')\n }\n >\n <div className=\"flex items-center justify-between gap-2\">\n <IconChip icon={<Icon className=\"h-4 w-4\" />} tint={meta.tint} />\n <StatusBadge status={s.status} />\n </div>\n <p className=\"mt-3 text-base font-semibold text-text-primary\">{meta.title}</p>\n <p className=\"mt-0.5 text-xs text-text-tertiary\">\n {meta.stampLabel} {formatRelativeTime(meta.stamp)}\n </p>\n\n <div className=\"mt-3 flex flex-wrap items-center gap-x-4 gap-y-1 text-xs text-text-secondary\">\n {s.caloriesBurned > 0 && (\n <span className=\"inline-flex items-center gap-1 tabular-nums\">\n <Flame className=\"h-3.5 w-3.5 text-status-warning-text\" />\n {formatKcal(s.caloriesBurned)}\n </span>\n )}\n {rpe != null && (\n <span className=\"inline-flex items-center gap-1 tabular-nums\">\n <Gauge className=\"h-3.5 w-3.5 text-accent\" />\n RPE {rpe}/10\n </span>\n )}\n {setCount != null && setCount > 0 && (\n <span className=\"inline-flex items-center gap-1 tabular-nums\">\n <ListChecks className=\"h-3.5 w-3.5 text-accent\" />\n {formatNumber(setCount)} sets\n </span>\n )}\n </div>\n\n {rpe != null && (\n <div className=\"mt-3\">\n <div className=\"mb-1 flex items-center justify-between text-[11px] text-text-tertiary\">\n <span>RPE intensity</span>\n <span className=\"tabular-nums\">{rpe}/10</span>\n </div>\n <Meter value={rpe * 10} height={6} />\n </div>\n )}\n\n {isLive && (\n <p className=\"mt-3 inline-flex items-center gap-1 text-xs font-semibold text-action-primary-bg\">\n In progress — Resume →\n </p>\n )}\n </GlassCard>\n </motion.button>\n );\n })}\n </div>\n\n <Pagination\n page={pager.page}\n pageSize={PAGE_SIZE}\n itemCount={rows.length}\n hasMore={hasMore}\n onPageChange={(t) => pager.onPageChange(t)}\n />\n </>\n )}\n\n {/* Start session dialog */}\n <ResponsiveDialog open={startOpen} onOpenChange={setStartOpen}>\n <ResponsiveDialogContent>\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>Start a workout session</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n Begin a live session now, or schedule one for later. Attach an active program to keep\n your training organised.\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n\n <div className=\"space-y-4 px-1\">\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-start-program\">Program (optional)</Label>\n <Select\n value={form.programId}\n onValueChange={(v) => setForm((f) => ({ ...f, programId: v }))}\n >\n <SelectTrigger id=\"myb-start-program\">\n <SelectValue placeholder=\"No program\" />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value={NO_PROGRAM}>No program</SelectItem>\n {activePrograms.map((p) => (\n <SelectItem key={p.id} value={p.id}>\n {p.name}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-start-scheduled\">Scheduled for (optional)</Label>\n <Input\n id=\"myb-start-scheduled\"\n type=\"datetime-local\"\n value={form.scheduledFor}\n onChange={(e) => setForm((f) => ({ ...f, scheduledFor: e.target.value }))}\n />\n <p className=\"text-xs text-text-tertiary\">\n Leave empty to start a live session right now.\n </p>\n </div>\n\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-start-notes\">Notes (optional)</Label>\n <Textarea\n id=\"myb-start-notes\"\n rows={3}\n value={form.notes}\n onChange={(e) => setForm((f) => ({ ...f, notes: e.target.value }))}\n placeholder=\"Focus, target lifts, how you're feeling…\"\n />\n </div>\n\n {formError && <p className=\"text-sm text-status-error-text\">{formError}</p>}\n </div>\n\n <ResponsiveDialogFooter>\n <Button\n variant=\"outline\"\n onClick={() => setStartOpen(false)}\n disabled={startSession.isPending}\n >\n Cancel\n </Button>\n <Button onClick={submitStart} disabled={startSession.isPending}>\n {startSession.isPending ? 'Starting…' : 'Start session'}\n </Button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"SessionsPage.js","names":[],"sources":["../../../../src/mybodyshield/pages/train/SessionsPage.tsx"],"sourcesContent":["/**\n * Train — live workout sessions.\n * @module mybodyshield/pages/train\n *\n * Lists the member's workout sessions (real `mybListSessions`) filtered by\n * status, and lets you start a new live session (optionally attached to an\n * active program). In-progress sessions are highlighted with an accent ring and\n * a \"Resume\" affordance. No mock data — everything comes from the myb* hooks.\n */\n\nimport { useState } from 'react';\nimport { motion, useReducedMotion } from 'framer-motion';\nimport {\n Plus,\n Flame,\n CheckCircle2,\n CalendarClock,\n PlayCircle,\n SkipForward,\n Gauge,\n ListChecks,\n} from 'lucide-react';\nimport {\n Button,\n GlassCard,\n Label,\n Textarea,\n Input,\n Select,\n SelectTrigger,\n SelectContent,\n SelectItem,\n SelectValue,\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n ResponsiveDialogDescription,\n ResponsiveDialogFooter,\n} from '@burdenoff/fe-libs/ui';\nimport {\n PageLayout,\n StatusBadge,\n FilterTabs,\n Pagination,\n LoadingView,\n ErrorView,\n EmptyState,\n type FilterOption,\n} from '../../components';\nimport { IconChip, Meter } from '../_shared/visuals';\nimport { useMyBodyShield } from '../../context';\nimport {\n useMybSessions,\n useMybStartSession,\n useMybPrograms,\n useOffsetPager,\n useMyBodyShieldAnalytics,\n} from '../../hooks';\nimport { formatRelativeTime, formatKcal, formatNumber } from '../../utils/formatters';\nimport type { MybSessionStatus, MybWorkoutSession } from '../../types';\n\ntype SessionFilterValue = MybSessionStatus | 'ALL';\n\n// Only statuses a control can actually reach are offered as filters: start →\n// IN_PROGRESS / SCHEDULED, complete → COMPLETED. Nothing moves a session to\n// SKIPPED, so that tab would always be empty. sessionMeta still handles the\n// SKIPPED case for display safety.\nconst STATUS_TABS: FilterOption<SessionFilterValue>[] = [\n { value: 'ALL', label: 'All' },\n { value: 'SCHEDULED', label: 'Scheduled' },\n { value: 'IN_PROGRESS', label: 'Live' },\n { value: 'COMPLETED', label: 'Completed' },\n];\n\nconst PAGE_SIZE = 24;\n\n/** Shared entrance easing — a soft, decelerating cubic-bezier. */\nconst MOTION_EASE: [number, number, number, number] = [0.16, 1, 0.3, 1];\n\n/** Sentinel for the optional program picker (Radix Select forbids an empty value). */\nconst NO_PROGRAM = '__none__';\n\ninterface StartForm {\n programId: string;\n scheduledFor: string;\n notes: string;\n}\n\nconst EMPTY_START_FORM: StartForm = { programId: NO_PROGRAM, scheduledFor: '', notes: '' };\n\ninterface SessionMeta {\n icon: typeof Flame;\n tint: string;\n title: string;\n stampLabel: string;\n stamp?: string | null;\n}\n\nfunction sessionMeta(s: MybWorkoutSession): SessionMeta {\n switch (s.status) {\n case 'IN_PROGRESS':\n return {\n icon: PlayCircle,\n tint: 'var(--color-accent-teal)',\n title: 'Live workout in progress',\n stampLabel: 'Started',\n stamp: s.startedAt ?? s.createdAt,\n };\n case 'COMPLETED':\n return {\n icon: CheckCircle2,\n tint: 'var(--color-status-success-text)',\n title:\n s.caloriesBurned > 0 ? `${formatKcal(s.caloriesBurned)} burned` : 'Workout completed',\n stampLabel: 'Completed',\n stamp: s.completedAt ?? s.startedAt ?? s.createdAt,\n };\n case 'SKIPPED':\n return {\n icon: SkipForward,\n tint: 'var(--color-text-secondary)',\n title: 'Skipped session',\n stampLabel: 'Scheduled',\n stamp: s.scheduledFor ?? s.createdAt,\n };\n case 'SCHEDULED':\n default:\n return {\n icon: CalendarClock,\n tint: 'var(--color-accent-primary)',\n title: 'Scheduled session',\n stampLabel: 'Scheduled',\n stamp: s.scheduledFor ?? s.createdAt,\n };\n }\n}\n\nexport function SessionsPage() {\n const { navigateTo, workspaceId } = useMyBodyShield();\n const reduce = useReducedMotion();\n const analytics = useMyBodyShieldAnalytics();\n\n const [filter, setFilter] = useState<SessionFilterValue>('ALL');\n const pager = useOffsetPager(PAGE_SIZE);\n\n const sessions = useMybSessions({\n status: filter === 'ALL' ? undefined : filter,\n pagination: { limit: PAGE_SIZE, offset: pager.offset },\n });\n\n const programs = useMybPrograms({ status: 'ACTIVE', pagination: { limit: 50 } });\n const activePrograms = programs.data?.items ?? [];\n\n const startSession = useMybStartSession();\n\n const [startOpen, setStartOpen] = useState(false);\n const [form, setForm] = useState<StartForm>(EMPTY_START_FORM);\n const [formError, setFormError] = useState<string | null>(null);\n\n const rows = sessions.data?.items ?? [];\n const hasMore = sessions.data?.pageInfo?.hasMore ?? false;\n\n const onFilterChange = (value: SessionFilterValue) => {\n setFilter(value);\n pager.reset();\n };\n\n const openStart = () => {\n setForm(EMPTY_START_FORM);\n setFormError(null);\n setStartOpen(true);\n analytics.emitIntent('session', 'create', { workspaceId });\n };\n\n const submitStart = async () => {\n setFormError(null);\n try {\n const created = await startSession.mutateAsync({\n programId: form.programId === NO_PROGRAM ? undefined : form.programId,\n scheduledFor: form.scheduledFor ? new Date(form.scheduledFor).toISOString() : undefined,\n notes: form.notes.trim() ? form.notes.trim() : undefined,\n });\n analytics.emitFact('session', 'started', { workspaceId, entityId: created.id });\n setStartOpen(false);\n navigateTo(`train/${created.id}`);\n } catch (e) {\n setFormError(e instanceof Error ? e.message : 'Failed to start the session.');\n }\n };\n\n return (\n <PageLayout\n title=\"Train\"\n description=\"Live workout sessions — start, log sets, and complete.\"\n actions={\n <Button onClick={openStart}>\n <Plus className=\"h-4 w-4\" /> Start session\n </Button>\n }\n >\n <div className=\"mb-6\">\n <FilterTabs options={STATUS_TABS} value={filter} onChange={onFilterChange} />\n </div>\n\n {sessions.isLoading ? (\n <LoadingView label=\"Loading your sessions…\" />\n ) : sessions.isError ? (\n <ErrorView\n message=\"We couldn't load your workout sessions.\"\n onRetry={() => sessions.refetch()}\n />\n ) : rows.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n icon={<Flame className=\"h-10 w-10\" />}\n title={filter === 'ALL' ? 'No sessions yet' : 'No sessions in this state'}\n description={\n filter === 'ALL'\n ? 'Start a workout session to begin logging sets and tracking your training.'\n : 'Try a different filter, or start a new workout session.'\n }\n action={\n <Button onClick={openStart}>\n <Plus className=\"h-4 w-4\" /> Start session\n </Button>\n }\n />\n ) : (\n <>\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n {rows.map((s, i) => {\n const meta = sessionMeta(s);\n const Icon = meta.icon;\n const isLive = s.status === 'IN_PROGRESS';\n const setCount = s.sets?.length;\n const rpe = s.perceivedExertion;\n return (\n <motion.button\n key={s.id}\n type=\"button\"\n onClick={() => navigateTo(`train/${s.id}`)}\n className=\"block w-full text-left\"\n initial={reduce ? false : { opacity: 0, y: 12 }}\n animate={{ opacity: 1, y: 0 }}\n transition={\n reduce\n ? { duration: 0 }\n : { duration: 0.35, delay: Math.min(i, 8) * 0.05, ease: MOTION_EASE }\n }\n whileHover={reduce ? undefined : { y: -3 }}\n whileTap={reduce ? undefined : { scale: 0.995 }}\n >\n <GlassCard\n treatment=\"plain\"\n glow={isLive}\n className={\n 'h-full p-4 transition-shadow hover:shadow-float' +\n (isLive\n ? ' ring-1 ring-inset ring-[color-mix(in_oklab,var(--color-action-primary-bg)_45%,transparent)]'\n : '')\n }\n >\n <div className=\"flex items-center justify-between gap-2\">\n <IconChip icon={<Icon className=\"h-4 w-4\" />} tint={meta.tint} />\n <StatusBadge status={s.status} />\n </div>\n <p className=\"mt-3 text-base font-semibold text-text-primary\">{meta.title}</p>\n <p className=\"mt-0.5 text-xs text-text-tertiary\">\n {meta.stampLabel} {formatRelativeTime(meta.stamp)}\n </p>\n\n <div className=\"mt-3 flex flex-wrap items-center gap-x-4 gap-y-1 text-xs text-text-secondary\">\n {s.caloriesBurned > 0 && (\n <span className=\"inline-flex items-center gap-1 tabular-nums\">\n <Flame className=\"h-3.5 w-3.5 text-status-warning-text\" />\n {formatKcal(s.caloriesBurned)}\n </span>\n )}\n {rpe != null && (\n <span className=\"inline-flex items-center gap-1 tabular-nums\">\n <Gauge className=\"h-3.5 w-3.5 text-accent\" />\n RPE {rpe}/10\n </span>\n )}\n {setCount != null && setCount > 0 && (\n <span className=\"inline-flex items-center gap-1 tabular-nums\">\n <ListChecks className=\"h-3.5 w-3.5 text-accent\" />\n {formatNumber(setCount)} sets\n </span>\n )}\n </div>\n\n {rpe != null && (\n <div className=\"mt-3\">\n <div className=\"mb-1 flex items-center justify-between text-[11px] text-text-tertiary\">\n <span>RPE intensity</span>\n <span className=\"tabular-nums\">{rpe}/10</span>\n </div>\n <Meter value={rpe * 10} height={6} />\n </div>\n )}\n\n {isLive && (\n <p className=\"mt-3 inline-flex items-center gap-1 text-xs font-semibold text-action-primary-bg\">\n In progress — Resume →\n </p>\n )}\n </GlassCard>\n </motion.button>\n );\n })}\n </div>\n\n <Pagination\n page={pager.page}\n pageSize={PAGE_SIZE}\n itemCount={rows.length}\n hasMore={hasMore}\n onPageChange={(t) => pager.onPageChange(t)}\n />\n </>\n )}\n\n {/* Start session dialog */}\n <ResponsiveDialog open={startOpen} onOpenChange={setStartOpen}>\n <ResponsiveDialogContent>\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>Start a workout session</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n Begin a live session now, or schedule one for later. Attach an active program to keep\n your training organised.\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n\n <div className=\"space-y-4 px-1\">\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-start-program\">Program (optional)</Label>\n <Select\n value={form.programId}\n onValueChange={(v) => setForm((f) => ({ ...f, programId: v }))}\n >\n <SelectTrigger id=\"myb-start-program\">\n <SelectValue placeholder=\"No program\" />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value={NO_PROGRAM}>No program</SelectItem>\n {activePrograms.map((p) => (\n <SelectItem key={p.id} value={p.id}>\n {p.name}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-start-scheduled\">Scheduled for (optional)</Label>\n <Input\n id=\"myb-start-scheduled\"\n type=\"datetime-local\"\n value={form.scheduledFor}\n onChange={(e) => setForm((f) => ({ ...f, scheduledFor: e.target.value }))}\n />\n <p className=\"text-xs text-text-tertiary\">\n Leave empty to start a live session right now.\n </p>\n </div>\n\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-start-notes\">Notes (optional)</Label>\n <Textarea\n id=\"myb-start-notes\"\n rows={3}\n value={form.notes}\n onChange={(e) => setForm((f) => ({ ...f, notes: e.target.value }))}\n placeholder=\"Focus, target lifts, how you're feeling…\"\n />\n </div>\n\n {formError && <p className=\"text-sm text-status-error-text\">{formError}</p>}\n </div>\n\n <ResponsiveDialogFooter>\n <Button\n variant=\"outline\"\n onClick={() => setStartOpen(false)}\n disabled={startSession.isPending}\n >\n Cancel\n </Button>\n <Button onClick={submitStart} disabled={startSession.isPending}>\n {startSession.isPending ? 'Starting…' : 'Start session'}\n </Button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n </PageLayout>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAoEA,IAAM,IAAkD;CACtD;EAAE,OAAO;EAAO,OAAO;EAAO;CAC9B;EAAE,OAAO;EAAa,OAAO;EAAa;CAC1C;EAAE,OAAO;EAAe,OAAO;EAAQ;CACvC;EAAE,OAAO;EAAa,OAAO;EAAa;CAC3C,EAEK,IAAY,IAGZ,IAAgD;CAAC;CAAM;CAAG;CAAK;CAAE,EAGjE,IAAa,YAQb,IAA8B;CAAE,WAAW;CAAY,cAAc;CAAI,OAAO;CAAI;AAU1F,SAAS,EAAY,GAAmC;AACtD,SAAQ,EAAE,QAAV;EACE,KAAK,cACH,QAAO;GACL,MAAM;GACN,MAAM;GACN,OAAO;GACP,YAAY;GACZ,OAAO,EAAE,aAAa,EAAE;GACzB;EACH,KAAK,YACH,QAAO;GACL,MAAM;GACN,MAAM;GACN,OACE,EAAE,iBAAiB,IAAI,GAAG,EAAW,EAAE,eAAe,CAAC,WAAW;GACpE,YAAY;GACZ,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE;GAC1C;EACH,KAAK,UACH,QAAO;GACL,MAAM;GACN,MAAM;GACN,OAAO;GACP,YAAY;GACZ,OAAO,EAAE,gBAAgB,EAAE;GAC5B;EAEH,QACE,QAAO;GACL,MAAM;GACN,MAAM;GACN,OAAO;GACP,YAAY;GACZ,OAAO,EAAE,gBAAgB,EAAE;GAC5B;;;AAIP,SAAgB,IAAe;CAC7B,IAAM,EAAE,eAAY,mBAAgB,GAAiB,EAC/C,IAAS,IAAkB,EAC3B,IAAY,GAA0B,EAEtC,CAAC,GAAQ,MAAa,EAA6B,MAAM,EACzD,IAAQ,GAAe,EAAU,EAEjC,IAAW,EAAe;EAC9B,QAAQ,MAAW,QAAQ,KAAA,IAAY;EACvC,YAAY;GAAE,OAAO;GAAW,QAAQ,EAAM;GAAQ;EACvD,CAAC,EAGI,KADW,EAAe;EAAE,QAAQ;EAAU,YAAY,EAAE,OAAO,IAAI;EAAE,CAAC,CAChD,MAAM,SAAS,EAAE,EAE3C,IAAe,IAAoB,EAEnC,CAAC,IAAW,KAAgB,EAAS,GAAM,EAC3C,CAAC,GAAM,KAAW,EAAoB,EAAiB,EACvD,CAAC,GAAW,KAAgB,EAAwB,KAAK,EAEzD,IAAO,EAAS,MAAM,SAAS,EAAE,EACjC,KAAU,EAAS,MAAM,UAAU,WAAW,IAE9C,MAAkB,MAA8B;AAEpD,EADA,GAAU,EAAM,EAChB,EAAM,OAAO;IAGT,UAAkB;AAItB,EAHA,EAAQ,EAAiB,EACzB,EAAa,KAAK,EAClB,EAAa,GAAK,EAClB,EAAU,WAAW,WAAW,UAAU,EAAE,gBAAa,CAAC;;AAmB5D,QACE,kBAAC,GAAD;EACE,OAAM;EACN,aAAY;EACZ,SACE,kBAAC,GAAD;GAAQ,SAAS;aAAjB,CACE,kBAAC,GAAD,EAAM,WAAU,WAAY,CAAA,EAAA,iBACrB;;YANb;GASE,kBAAC,OAAD;IAAK,WAAU;cACb,kBAAC,GAAD;KAAY,SAAS;KAAa,OAAO;KAAQ,UAAU;KAAkB,CAAA;IACzE,CAAA;GAEL,EAAS,YACR,kBAAC,GAAD,EAAa,OAAM,0BAA2B,CAAA,GAC5C,EAAS,UACX,kBAAC,GAAD;IACE,SAAQ;IACR,eAAe,EAAS,SAAS;IACjC,CAAA,GACA,EAAK,WAAW,IAClB,kBAAC,GAAD;IACE,cAAa;IACb,MAAM,kBAAC,GAAD,EAAO,WAAU,aAAc,CAAA;IACrC,OAAO,MAAW,QAAQ,oBAAoB;IAC9C,aACE,MAAW,QACP,8EACA;IAEN,QACE,kBAAC,GAAD;KAAQ,SAAS;eAAjB,CACE,kBAAC,GAAD,EAAM,WAAU,WAAY,CAAA,EAAA,iBACrB;;IAEX,CAAA,GAEF,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,OAAD;IAAK,WAAU;cACZ,EAAK,KAAK,GAAG,MAAM;KAClB,IAAM,IAAO,EAAY,EAAE,EACrB,IAAO,EAAK,MACZ,IAAS,EAAE,WAAW,eACtB,IAAW,EAAE,MAAM,QACnB,IAAM,EAAE;AACd,YACE,kBAAC,EAAO,QAAR;MAEE,MAAK;MACL,eAAe,EAAW,SAAS,EAAE,KAAK;MAC1C,WAAU;MACV,SAAS,IAAS,KAAQ;OAAE,SAAS;OAAG,GAAG;OAAI;MAC/C,SAAS;OAAE,SAAS;OAAG,GAAG;OAAG;MAC7B,YACE,IACI,EAAE,UAAU,GAAG,GACf;OAAE,UAAU;OAAM,OAAO,KAAK,IAAI,GAAG,EAAE,GAAG;OAAM,MAAM;OAAa;MAEzE,YAAY,IAAS,KAAA,IAAY,EAAE,GAAG,IAAI;MAC1C,UAAU,IAAS,KAAA,IAAY,EAAE,OAAO,MAAO;gBAE/C,kBAAC,IAAD;OACE,WAAU;OACV,MAAM;OACN,WACE,qDACC,IACG,iGACA;iBAPR;QAUE,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,GAAD;UAAU,MAAM,kBAAC,GAAD,EAAM,WAAU,WAAY,CAAA;UAAE,MAAM,EAAK;UAAQ,CAAA,EACjE,kBAAC,IAAD,EAAa,QAAQ,EAAE,QAAU,CAAA,CAC7B;;QACN,kBAAC,KAAD;SAAG,WAAU;mBAAkD,EAAK;SAAU,CAAA;QAC9E,kBAAC,KAAD;SAAG,WAAU;mBAAb;UACG,EAAK;UAAW;UAAE,GAAmB,EAAK,MAAM;UAC/C;;QAEJ,kBAAC,OAAD;SAAK,WAAU;mBAAf;UACG,EAAE,iBAAiB,KAClB,kBAAC,QAAD;WAAM,WAAU;qBAAhB,CACE,kBAAC,GAAD,EAAO,WAAU,wCAAyC,CAAA,EACzD,EAAW,EAAE,eAAe,CACxB;;UAER,KAAO,QACN,kBAAC,QAAD;WAAM,WAAU;qBAAhB;YACE,kBAAC,IAAD,EAAO,WAAU,2BAA4B,CAAA;;YACxC;YAAI;YACJ;;UAER,KAAY,QAAQ,IAAW,KAC9B,kBAAC,QAAD;WAAM,WAAU;qBAAhB;YACE,kBAAC,IAAD,EAAY,WAAU,2BAA4B,CAAA;YACjD,GAAa,EAAS;YAAC;YACnB;;UAEL;;QAEL,KAAO,QACN,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,OAAD;UAAK,WAAU;oBAAf,CACE,kBAAC,QAAD,EAAA,UAAM,iBAAoB,CAAA,EAC1B,kBAAC,QAAD;WAAM,WAAU;qBAAhB,CAAgC,GAAI,MAAU;aAC1C;aACN,kBAAC,IAAD;UAAO,OAAO,IAAM;UAAI,QAAQ;UAAK,CAAA,CACjC;;QAGP,KACC,kBAAC,KAAD;SAAG,WAAU;mBAAmF;SAE5F,CAAA;QAEI;;MACE,EAtET,EAAE,GAsEO;MAElB;IACE,CAAA,EAEN,kBAAC,GAAD;IACE,MAAM,EAAM;IACZ,UAAU;IACV,WAAW,EAAK;IACP;IACT,eAAe,MAAM,EAAM,aAAa,EAAE;IAC1C,CAAA,CACD,EAAA,CAAA;GAIL,kBAAC,GAAD;IAAkB,MAAM;IAAW,cAAc;cAC/C,kBAAC,GAAD,EAAA,UAAA;KACE,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD,EAAA,UAAuB,2BAA+C,CAAA,EACtE,kBAAC,GAAD,EAAA,UAA6B,kHAGC,CAAA,CACP,EAAA,CAAA;KAEzB,kBAAC,OAAD;MAAK,WAAU;gBAAf;OACE,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,GAAD;SAAO,SAAQ;mBAAoB;SAA0B,CAAA,EAC7D,kBAAC,GAAD;SACE,OAAO,EAAK;SACZ,gBAAgB,MAAM,GAAS,OAAO;UAAE,GAAG;UAAG,WAAW;UAAG,EAAE;mBAFhE,CAIE,kBAAC,GAAD;UAAe,IAAG;oBAChB,kBAAC,GAAD,EAAa,aAAY,cAAe,CAAA;UAC1B,CAAA,EAChB,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD;UAAY,OAAO;oBAAY;UAAuB,CAAA,EACrD,GAAe,KAAK,MACnB,kBAAC,GAAD;UAAuB,OAAO,EAAE;oBAC7B,EAAE;UACQ,EAFI,EAAE,GAEN,CACb,CACY,EAAA,CAAA,CACT;WACL;;OAEN,kBAAC,OAAD;QAAK,WAAU;kBAAf;SACE,kBAAC,GAAD;UAAO,SAAQ;oBAAsB;UAAgC,CAAA;SACrE,kBAAC,IAAD;UACE,IAAG;UACH,MAAK;UACL,OAAO,EAAK;UACZ,WAAW,MAAM,GAAS,OAAO;WAAE,GAAG;WAAG,cAAc,EAAE,OAAO;WAAO,EAAE;UACzE,CAAA;SACF,kBAAC,KAAD;UAAG,WAAU;oBAA6B;UAEtC,CAAA;SACA;;OAEN,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,GAAD;SAAO,SAAQ;mBAAkB;SAAwB,CAAA,EACzD,kBAAC,GAAD;SACE,IAAG;SACH,MAAM;SACN,OAAO,EAAK;SACZ,WAAW,MAAM,GAAS,OAAO;UAAE,GAAG;UAAG,OAAO,EAAE,OAAO;UAAO,EAAE;SAClE,aAAY;SACZ,CAAA,CACE;;OAEL,KAAa,kBAAC,KAAD;QAAG,WAAU;kBAAkC;QAAc,CAAA;OACvE;;KAEN,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD;MACE,SAAQ;MACR,eAAe,EAAa,GAAM;MAClC,UAAU,EAAa;gBACxB;MAEQ,CAAA,EACT,kBAAC,GAAD;MAAQ,SAxNE,YAAY;AAC9B,SAAa,KAAK;AAClB,WAAI;QACF,IAAM,IAAU,MAAM,EAAa,YAAY;SAC7C,WAAW,EAAK,cAAc,IAAa,KAAA,IAAY,EAAK;SAC5D,cAAc,EAAK,eAAe,IAAI,KAAK,EAAK,aAAa,CAAC,aAAa,GAAG,KAAA;SAC9E,OAAO,EAAK,MAAM,MAAM,GAAG,EAAK,MAAM,MAAM,GAAG,KAAA;SAChD,CAAC;AAGF,QAFA,EAAU,SAAS,WAAW,WAAW;SAAE;SAAa,UAAU,EAAQ;SAAI,CAAC,EAC/E,EAAa,GAAM,EACnB,EAAW,SAAS,EAAQ,KAAK;gBAC1B,GAAG;AACV,UAAa,aAAa,QAAQ,EAAE,UAAU,+BAA+B;;;MA4MzC,UAAU,EAAa;gBAClD,EAAa,YAAY,cAAc;MACjC,CAAA,CACc,EAAA,CAAA;KACD,EAAA,CAAA;IACT,CAAA;GACR"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* mybodyshield adapter for the shared fe-libs AssistantWidget transport.
|
|
3
|
+
*
|
|
4
|
+
* The sandbox lifecycle + poll/stream loop now lives in
|
|
5
|
+
* `@burdenoff/fe-libs/shared/assistant` (one implementation for all products);
|
|
6
|
+
* this is a thin wrapper. The exported name/shape are unchanged so app-shell
|
|
7
|
+
* imports don't move. The public return type is a LOCALLY-declared structural
|
|
8
|
+
* mirror — never re-export fe-libs types (vite-plugin-dts path rewriting).
|
|
9
|
+
*/
|
|
10
|
+
interface AssistantSendArgs {
|
|
11
|
+
prompt: string;
|
|
12
|
+
onProgress: (partialText: string) => void;
|
|
13
|
+
signal: AbortSignal;
|
|
14
|
+
}
|
|
15
|
+
export interface AssistantTransport {
|
|
16
|
+
sendPrompt: (args: AssistantSendArgs) => Promise<{
|
|
17
|
+
text: string;
|
|
18
|
+
}>;
|
|
19
|
+
prewarm?: (level?: 'probe' | 'full') => void;
|
|
20
|
+
suspend?: () => void;
|
|
21
|
+
}
|
|
22
|
+
export declare function useSandboxAssistantTransport(): AssistantTransport;
|
|
23
|
+
export {};
|
|
24
|
+
//# sourceMappingURL=createSandboxAssistantTransport.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"createSandboxAssistantTransport.d.ts","sourceRoot":"","sources":["../../../src/mybodyshield/services/createSandboxAssistantTransport.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAKH,UAAU,iBAAiB;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1C,MAAM,EAAE,WAAW,CAAC;CACrB;AAED,MAAM,WAAW,kBAAkB;IACjC,UAAU,EAAE,CAAC,IAAI,EAAE,iBAAiB,KAAK,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACnE,OAAO,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE,OAAO,GAAG,MAAM,KAAK,IAAI,CAAC;IAC7C,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;CACtB;AAED,wBAAgB,4BAA4B,IAAI,kBAAkB,CAMjE"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { gatherPageContext as e } from "../utils/pageContext.js";
|
|
2
|
+
import { useSandboxAssistantTransport as t } from "@burdenoff/fe-libs/shared/assistant";
|
|
3
|
+
//#region src/mybodyshield/services/createSandboxAssistantTransport.ts
|
|
4
|
+
function n() {
|
|
5
|
+
return t({
|
|
6
|
+
product: "mybodyshield",
|
|
7
|
+
gatherPageContext: e,
|
|
8
|
+
enableCredits: !0
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
//#endregion
|
|
12
|
+
export { n as useSandboxAssistantTransport };
|
|
13
|
+
|
|
14
|
+
//# sourceMappingURL=createSandboxAssistantTransport.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"createSandboxAssistantTransport.js","names":[],"sources":["../../../src/mybodyshield/services/createSandboxAssistantTransport.ts"],"sourcesContent":["/**\n * mybodyshield adapter for the shared fe-libs AssistantWidget transport.\n *\n * The sandbox lifecycle + poll/stream loop now lives in\n * `@burdenoff/fe-libs/shared/assistant` (one implementation for all products);\n * this is a thin wrapper. The exported name/shape are unchanged so app-shell\n * imports don't move. The public return type is a LOCALLY-declared structural\n * mirror — never re-export fe-libs types (vite-plugin-dts path rewriting).\n */\n\nimport { useSandboxAssistantTransport as useSharedTransport } from '@burdenoff/fe-libs/shared/assistant';\nimport { gatherPageContext } from '#mybodyshield/utils/pageContext';\n\ninterface AssistantSendArgs {\n prompt: string;\n onProgress: (partialText: string) => void;\n signal: AbortSignal;\n}\n\nexport interface AssistantTransport {\n sendPrompt: (args: AssistantSendArgs) => Promise<{ text: string }>;\n prewarm?: (level?: 'probe' | 'full') => void;\n suspend?: () => void;\n}\n\nexport function useSandboxAssistantTransport(): AssistantTransport {\n return useSharedTransport({\n product: 'mybodyshield',\n gatherPageContext: gatherPageContext,\n enableCredits: true,\n });\n}\n"],"mappings":";;;AAyBA,SAAgB,IAAmD;AACjE,QAAO,EAAmB;EACxB,SAAS;EACU;EACnB,eAAe;EAChB,CAAC"}
|