@burdenoff/microfe-mybodyshield 2026.731.2 → 2026.801.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/mybodyshield/MyBodyShieldRoutes.js +1 -1
- package/dist/mybodyshield/MyBodyShieldRoutes.js.map +1 -1
- package/dist/mybodyshield/context/MyBodyShieldContext.js +1 -1
- package/dist/mybodyshield/context/MyBodyShieldContext.js.map +1 -1
- package/dist/mybodyshield/pages/programs/ProgramDetailPage.js +1 -1
- package/dist/mybodyshield/pages/programs/ProgramDetailPage.js.map +1 -1
- package/dist/mybodyshield/pages/train/SessionDetailPage.js +1 -1
- package/dist/mybodyshield/pages/train/SessionDetailPage.js.map +1 -1
- package/package.json +3 -3
|
@@ -4,7 +4,7 @@ import { PageDisabled as n } from "./components/PageDisabled.js";
|
|
|
4
4
|
import "./components/index.js";
|
|
5
5
|
import { DashboardPage as r } from "./pages/DashboardPage.js";
|
|
6
6
|
import { Suspense as i, lazy as a } from "react";
|
|
7
|
-
import { Navigate as o, Route as s, Routes as c, useLocation as l } from "react-router
|
|
7
|
+
import { Navigate as o, Route as s, Routes as c, useLocation as l } from "react-router";
|
|
8
8
|
import { jsx as u, jsxs as d } from "react/jsx-runtime";
|
|
9
9
|
import { Spinner as f } from "@burdenoff/fe-libs/ui";
|
|
10
10
|
//#region src/mybodyshield/MyBodyShieldRoutes.tsx
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MyBodyShieldRoutes.js","names":[],"sources":["../../src/mybodyshield/MyBodyShieldRoutes.tsx"],"sourcesContent":["/**\n * MyBodyShield Routes\n * @module mybodyshield\n *\n * Domain routes. The dashboard is eager-loaded (primary landing); all other\n * pages are lazy-loaded. Every route maps to a real `myb*` backend capability.\n */\n\nimport { type FC, lazy, Suspense, type ReactNode } from 'react';\nimport { Routes, Route, Navigate, useLocation } from 'react-router
|
|
1
|
+
{"version":3,"file":"MyBodyShieldRoutes.js","names":[],"sources":["../../src/mybodyshield/MyBodyShieldRoutes.tsx"],"sourcesContent":["/**\n * MyBodyShield Routes\n * @module mybodyshield\n *\n * Domain routes. The dashboard is eager-loaded (primary landing); all other\n * pages are lazy-loaded. Every route maps to a real `myb*` backend capability.\n */\n\nimport { type FC, lazy, Suspense, type ReactNode } from 'react';\nimport { Routes, Route, Navigate, useLocation } from 'react-router';\nimport { Spinner } from '@burdenoff/fe-libs/ui';\nimport { useMyBodyShield } from './context/MyBodyShieldContext';\nimport { FeatureFlagGuard, PageDisabled } from './components';\n\n// Eager — primary landing\nimport { DashboardPage } from './pages/DashboardPage';\n\n// Programs (Training)\nconst ProgramsPage = lazy(() =>\n import('./pages/programs/ProgramsPage').then((m) => ({ default: m.ProgramsPage }))\n);\nconst ProgramDetailPage = lazy(() =>\n import('./pages/programs/ProgramDetailPage').then((m) => ({ default: m.ProgramDetailPage }))\n);\n\n// Train (live workout sessions & sets)\nconst SessionsPage = lazy(() =>\n import('./pages/train/SessionsPage').then((m) => ({ default: m.SessionsPage }))\n);\nconst SessionDetailPage = lazy(() =>\n import('./pages/train/SessionDetailPage').then((m) => ({ default: m.SessionDetailPage }))\n);\n\n// Nutrition (plans, meals, macros)\nconst NutritionPage = lazy(() =>\n import('./pages/nutrition/NutritionPage').then((m) => ({ default: m.NutritionPage }))\n);\n\n// Goals (Progress)\nconst GoalsPage = lazy(() =>\n import('./pages/goals/GoalsPage').then((m) => ({ default: m.GoalsPage }))\n);\n\n// Progress (body metrics)\nconst ProgressPage = lazy(() =>\n import('./pages/progress/ProgressPage').then((m) => ({ default: m.ProgressPage }))\n);\n\n// Recovery (Wellbeing)\nconst RecoveryPage = lazy(() =>\n import('./pages/recovery/RecoveryPage').then((m) => ({ default: m.RecoveryPage }))\n);\n\n// Coaching (Wellbeing)\nconst CoachingPage = lazy(() =>\n import('./pages/coaching/CoachingPage').then((m) => ({ default: m.CoachingPage }))\n);\n\nconst PageLoader: FC = () => (\n <div className=\"flex min-h-[400px] items-center justify-center\">\n <Spinner size=\"lg\" />\n </div>\n);\n\n/** Wrap a page element with its feature-flag gate + analytics page-view. */\nfunction PageGate({ page, children }: { page: string; children: ReactNode }): React.ReactElement {\n const title = page\n .split('_')\n .map((word) => word.charAt(0).toUpperCase() + word.slice(1))\n .join(' ');\n return (\n <FeatureFlagGuard page={page} fallback={<PageDisabled title={title} />}>\n {children}\n </FeatureFlagGuard>\n );\n}\n\nexport const MyBodyShieldRoutes: FC = () => {\n const { basePath, isStandalone } = useMyBodyShield();\n const location = useLocation();\n\n const normalizedBasePath = basePath === '/' ? '' : (basePath ?? '').replace(/\\/$/, '');\n\n // Guard: only render if the current path matches basePath (integrated mode).\n if (normalizedBasePath && !isStandalone) {\n const pathMatches =\n location.pathname === normalizedBasePath ||\n location.pathname.startsWith(normalizedBasePath + '/');\n if (!pathMatches) {\n return null;\n }\n }\n\n return (\n <div className=\"flex min-h-full flex-col\">\n <Suspense fallback={<PageLoader />}>\n <Routes>\n <Route\n index\n element={\n <PageGate page=\"dashboard\">\n <DashboardPage />\n </PageGate>\n }\n />\n\n <Route\n path=\"programs\"\n element={\n <PageGate page=\"programs\">\n <ProgramsPage />\n </PageGate>\n }\n />\n <Route\n path=\"programs/:id\"\n element={\n <PageGate page=\"program_detail\">\n <ProgramDetailPage />\n </PageGate>\n }\n />\n\n <Route\n path=\"train\"\n element={\n <PageGate page=\"train\">\n <SessionsPage />\n </PageGate>\n }\n />\n <Route\n path=\"train/:id\"\n element={\n <PageGate page=\"session_detail\">\n <SessionDetailPage />\n </PageGate>\n }\n />\n\n <Route\n path=\"nutrition\"\n element={\n <PageGate page=\"nutrition\">\n <NutritionPage />\n </PageGate>\n }\n />\n\n <Route\n path=\"goals\"\n element={\n <PageGate page=\"goals\">\n <GoalsPage />\n </PageGate>\n }\n />\n\n <Route\n path=\"progress\"\n element={\n <PageGate page=\"progress\">\n <ProgressPage />\n </PageGate>\n }\n />\n\n <Route\n path=\"recovery\"\n element={\n <PageGate page=\"recovery\">\n <RecoveryPage />\n </PageGate>\n }\n />\n\n <Route\n path=\"coaching\"\n element={\n <PageGate page=\"coaching\">\n <CoachingPage />\n </PageGate>\n }\n />\n\n <Route path=\"*\" element={<Navigate to=\"\" replace />} />\n </Routes>\n </Suspense>\n </div>\n );\n};\n"],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { joinPath as e } from "../utils/navigation.js";
|
|
2
2
|
import { createContext as t, useCallback as n, useContext as r, useMemo as i } from "react";
|
|
3
|
-
import { MemoryRouter as a, useNavigate as o } from "react-router
|
|
3
|
+
import { MemoryRouter as a, useNavigate as o } from "react-router";
|
|
4
4
|
import { jsx as s } from "react/jsx-runtime";
|
|
5
5
|
//#region src/mybodyshield/context/MyBodyShieldContext.tsx
|
|
6
6
|
var c = t(null);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MyBodyShieldContext.js","names":[],"sources":["../../../src/mybodyshield/context/MyBodyShieldContext.tsx"],"sourcesContent":["/**\n * MyBodyShield Context Provider\n * @module mybodyshield/context\n *\n * Splits standalone mode (wraps its own MemoryRouter + useNavigate) from\n * integrated mode (the host shell supplies `navigate`). Exposes gateway/auth\n * context + relative navigation helpers to every page.\n */\n\nimport type { FC, PropsWithChildren } from 'react';\nimport { createContext, useContext, useMemo, useCallback } from 'react';\nimport { MemoryRouter, useNavigate as useRouterNavigate } from 'react-router
|
|
1
|
+
{"version":3,"file":"MyBodyShieldContext.js","names":[],"sources":["../../../src/mybodyshield/context/MyBodyShieldContext.tsx"],"sourcesContent":["/**\n * MyBodyShield Context Provider\n * @module mybodyshield/context\n *\n * Splits standalone mode (wraps its own MemoryRouter + useNavigate) from\n * integrated mode (the host shell supplies `navigate`). Exposes gateway/auth\n * context + relative navigation helpers to every page.\n */\n\nimport type { FC, PropsWithChildren } from 'react';\nimport { createContext, useContext, useMemo, useCallback } from 'react';\nimport { MemoryRouter, useNavigate as useRouterNavigate } from 'react-router';\nimport type { MyBodyShieldRootProps } from '../types';\nimport { joinPath } from '../utils/navigation';\n\ninterface MyBodyShieldContextValue extends Omit<\n MyBodyShieldRootProps,\n 'registerNavItems' | 'registerFooterItems' | 'navigate'\n> {\n isStandalone: boolean;\n /** Shell's navigate function (only available in integrated mode). */\n navigate?: (path: string) => void;\n /** Navigate to a path relative to basePath. */\n navigateTo: (relativePath: string) => void;\n /** Get the full path for a relative path. */\n getPath: (relativePath: string) => string;\n}\n\nconst MyBodyShieldContext = createContext<MyBodyShieldContextValue | null>(null);\n\nexport function useMyBodyShield(): MyBodyShieldContextValue {\n const context = useContext(MyBodyShieldContext);\n if (!context) {\n throw new Error('useMyBodyShield must be used within an MyBodyShieldProvider');\n }\n return context;\n}\n\ntype ProviderProps = Omit<MyBodyShieldRootProps, 'registerNavItems' | 'registerFooterItems'> & {\n isStandalone: boolean;\n};\n\nconst StandaloneProviderInner: FC<PropsWithChildren<ProviderProps>> = ({\n children,\n basePath,\n currentUser,\n tenantId,\n workspaceId,\n apiGatewayUrl,\n authToken,\n}) => {\n const routerNavigate = useRouterNavigate();\n const effectiveBasePath = basePath || '/';\n\n const getPath = useCallback(\n (relativePath: string) => joinPath(effectiveBasePath, relativePath),\n [effectiveBasePath]\n );\n\n const navigateTo = useCallback(\n (relativePath: string) => {\n routerNavigate(joinPath(effectiveBasePath, relativePath));\n },\n [effectiveBasePath, routerNavigate]\n );\n\n const contextValue = useMemo<MyBodyShieldContextValue>(\n () => ({\n basePath: effectiveBasePath,\n navigate: undefined,\n currentUser,\n tenantId,\n workspaceId,\n apiGatewayUrl,\n authToken,\n isStandalone: true,\n navigateTo,\n getPath,\n }),\n [\n effectiveBasePath,\n currentUser,\n tenantId,\n workspaceId,\n apiGatewayUrl,\n authToken,\n navigateTo,\n getPath,\n ]\n );\n\n return (\n <MyBodyShieldContext.Provider value={contextValue}>{children}</MyBodyShieldContext.Provider>\n );\n};\n\nconst IntegratedProviderInner: FC<\n PropsWithChildren<ProviderProps & { navigate: (path: string) => void }>\n> = ({\n children,\n basePath,\n navigate,\n currentUser,\n tenantId,\n workspaceId,\n apiGatewayUrl,\n authToken,\n}) => {\n const effectiveBasePath = basePath || '/';\n\n const getPath = useCallback(\n (relativePath: string) => joinPath(effectiveBasePath, relativePath),\n [effectiveBasePath]\n );\n\n const navigateTo = useCallback(\n (relativePath: string) => {\n navigate(joinPath(effectiveBasePath, relativePath));\n },\n [effectiveBasePath, navigate]\n );\n\n const contextValue = useMemo<MyBodyShieldContextValue>(\n () => ({\n basePath: effectiveBasePath,\n navigate,\n currentUser,\n tenantId,\n workspaceId,\n apiGatewayUrl,\n authToken,\n isStandalone: false,\n navigateTo,\n getPath,\n }),\n [\n effectiveBasePath,\n navigate,\n currentUser,\n tenantId,\n workspaceId,\n apiGatewayUrl,\n authToken,\n navigateTo,\n getPath,\n ]\n );\n\n return (\n <MyBodyShieldContext.Provider value={contextValue}>{children}</MyBodyShieldContext.Provider>\n );\n};\n\nexport const MyBodyShieldProvider: FC<PropsWithChildren<MyBodyShieldRootProps>> = (props) => {\n const { children, navigate } = props;\n const isStandalone = !navigate;\n\n if (isStandalone) {\n return (\n <MemoryRouter>\n <StandaloneProviderInner {...props} isStandalone={true}>\n {children}\n </StandaloneProviderInner>\n </MemoryRouter>\n );\n }\n\n return (\n <IntegratedProviderInner {...props} navigate={navigate} isStandalone={false}>\n {children}\n </IntegratedProviderInner>\n );\n};\n"],"mappings":";;;;;AA4BA,IAAM,IAAsB,EAA+C,KAAK;AAEhF,SAAgB,IAA4C;CAC1D,IAAM,IAAU,EAAW,EAAoB;AAC/C,KAAI,CAAC,EACH,OAAU,MAAM,8DAA8D;AAEhF,QAAO;;AAOT,IAAM,KAAiE,EACrE,aACA,aACA,gBACA,aACA,gBACA,kBACA,mBACI;CACJ,IAAM,IAAiB,GAAmB,EACpC,IAAoB,KAAY,KAEhC,IAAU,GACb,MAAyB,EAAS,GAAmB,EAAa,EACnE,CAAC,EAAkB,CACpB,EAEK,IAAa,GAChB,MAAyB;AACxB,IAAe,EAAS,GAAmB,EAAa,CAAC;IAE3D,CAAC,GAAmB,EAAe,CACpC,EAEK,IAAe,SACZ;EACL,UAAU;EACV,UAAU,KAAA;EACV;EACA;EACA;EACA;EACA;EACA,cAAc;EACd;EACA;EACD,GACD;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD,CACF;AAED,QACE,kBAAC,EAAoB,UAArB;EAA8B,OAAO;EAAe;EAAwC,CAAA;GAI1F,KAED,EACH,aACA,aACA,aACA,gBACA,aACA,gBACA,kBACA,mBACI;CACJ,IAAM,IAAoB,KAAY,KAEhC,IAAU,GACb,MAAyB,EAAS,GAAmB,EAAa,EACnE,CAAC,EAAkB,CACpB,EAEK,IAAa,GAChB,MAAyB;AACxB,IAAS,EAAS,GAAmB,EAAa,CAAC;IAErD,CAAC,GAAmB,EAAS,CAC9B,EAEK,IAAe,SACZ;EACL,UAAU;EACV;EACA;EACA;EACA;EACA;EACA;EACA,cAAc;EACd;EACA;EACD,GACD;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD,CACF;AAED,QACE,kBAAC,EAAoB,UAArB;EAA8B,OAAO;EAAe;EAAwC,CAAA;GAInF,KAAsE,MAAU;CAC3F,IAAM,EAAE,aAAU,gBAAa;AAa/B,QAZsB,IAapB,kBAAC,GAAD;EAAyB,GAAI;EAAiB;EAAU,cAAc;EACnE;EACuB,CAAA,GAXxB,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD;EAAyB,GAAI;EAAO,cAAc;EAC/C;EACuB,CAAA,EACb,CAAA"}
|
|
@@ -18,7 +18,7 @@ import { useReducedMotion as he } from "../../../node_modules/framer-motion/dist
|
|
|
18
18
|
import { GOAL_TYPE_META as ge, IconChip as _e, MUSCLE_META as u, WORKOUT_TYPE_META as d } from "../_shared/visuals.js";
|
|
19
19
|
import { useMemo as ve, useState as f } from "react";
|
|
20
20
|
import { Archive as ye, ArrowLeft as be, CalendarDays as p, CheckCircle2 as xe, Clock as m, Dumbbell as Se, Flame as Ce, Lock as we, Pencil as Te, Play as Ee, Plus as h } from "lucide-react";
|
|
21
|
-
import { useParams as De } from "react-router
|
|
21
|
+
import { useParams as De } from "react-router";
|
|
22
22
|
import { Fragment as Oe, jsx as g, jsxs as _ } from "react/jsx-runtime";
|
|
23
23
|
import { Button as v, GlassCard as ke, Input as y, Label as b, ResponsiveDialog as x, ResponsiveDialogContent as S, ResponsiveDialogDescription as C, ResponsiveDialogFooter as w, ResponsiveDialogHeader as T, ResponsiveDialogTitle as E, Select as Ae, SelectContent as D, SelectItem as O, SelectTrigger as k, SelectValue as A, Textarea as je } from "@burdenoff/fe-libs/ui";
|
|
24
24
|
//#region src/mybodyshield/pages/programs/ProgramDetailPage.tsx
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ProgramDetailPage.js","names":[],"sources":["../../../../src/mybodyshield/pages/programs/ProgramDetailPage.tsx"],"sourcesContent":["/**\n * Program detail — a single training block with its workouts & exercises.\n * @module mybodyshield/pages/programs\n *\n * Renders one `mybProgram` (with its nested `workouts[].exercises[]`), lifecycle\n * transition controls (activate / complete / archive) and dialogs to add\n * workouts and exercises. All data & mutations hit the real `myb*` contract.\n */\n\nimport { useMemo, useState, type FC, type ReactNode } from 'react';\nimport { motion, useReducedMotion } from 'framer-motion';\nimport { useParams } from 'react-router-dom';\nimport {\n ArrowLeft,\n Plus,\n CheckCircle2,\n Archive,\n Play,\n Dumbbell,\n Pencil,\n Flame,\n Lock,\n Clock,\n CalendarDays,\n} from 'lucide-react';\nimport {\n Button,\n Input,\n Label,\n Textarea,\n Select,\n SelectTrigger,\n SelectContent,\n SelectItem,\n SelectValue,\n GlassCard,\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n ResponsiveDialogDescription,\n ResponsiveDialogFooter,\n} from '@burdenoff/fe-libs/ui';\nimport {\n PageLayout,\n StatusBadge,\n EmptyState,\n LoadingView,\n ErrorView,\n Section,\n EvidenceUpload,\n FileLink,\n MybDiscussion,\n MybTags,\n} from '../../components';\nimport { IconChip, WORKOUT_TYPE_META, MUSCLE_META, GOAL_TYPE_META } from '../_shared/visuals';\nimport { useMyBodyShield } from '../../context';\nimport {\n useMybProgram,\n useMybUpdateProgram,\n useMybAddWorkout,\n useMybAddExercise,\n useMybTransitionProgram,\n useMybArchiveProgram,\n useMyBodyShieldAnalytics,\n type UploadedMybFile,\n} from '../../hooks';\nimport {\n formatEnumLabel,\n formatDate,\n formatNumber,\n formatWeight,\n formatDuration,\n formatMinutes,\n} from '../../utils/formatters';\nimport type {\n MybWorkout,\n MybExercise,\n MybWorkoutType,\n MybMuscleGroup,\n MybProgramStatus,\n MybGoalType,\n MybFitnessLevel,\n MybProgramPhase,\n MybCreateWorkoutInput,\n MybCreateExerciseInput,\n MybUpdateProgramInput,\n} from '../../types';\n\nconst WORKOUT_TYPES: readonly MybWorkoutType[] = [\n 'STRENGTH',\n 'CARDIO',\n 'HIIT',\n 'MOBILITY',\n 'YOGA',\n 'RECOVERY',\n];\nconst MUSCLE_GROUPS: readonly MybMuscleGroup[] = [\n 'CHEST',\n 'BACK',\n 'LEGS',\n 'SHOULDERS',\n 'ARMS',\n 'CORE',\n 'FULL_BODY',\n];\n\n/** Parse a numeric text field → number | null (null when blank / invalid). */\nconst toNum = (raw: string): number | null => {\n const t = raw.trim();\n if (t === '') return null;\n const n = Number(t);\n return Number.isFinite(n) ? n : null;\n};\n\n/** Shared entrance easing — a soft, decelerating cubic-bezier. */\nconst MOTION_EASE: [number, number, number, number] = [0.16, 1, 0.3, 1];\n\nconst STAGGER_CONTAINER = {\n hidden: {},\n show: { transition: { staggerChildren: 0.06 } },\n};\nconst RISE_ITEM = {\n hidden: { opacity: 0, y: 12 },\n show: { opacity: 1, y: 0, transition: { duration: 0.35, ease: MOTION_EASE } },\n};\n\nconst Chip: FC<{ children: ReactNode; icon?: ReactNode; tint?: string }> = ({\n children,\n icon,\n tint,\n}) => (\n <span\n className=\"inline-flex items-center gap-1.5 rounded-full bg-bg-sunken px-2.5 py-1 text-xs font-medium text-text-secondary\"\n style={tint ? { color: tint } : undefined}\n >\n {icon}\n {children}\n </span>\n);\n\nconst MuscleChip: FC<{ group: MybMuscleGroup }> = ({ group }) => {\n const tint = MUSCLE_META[group].tint;\n return (\n <span\n className=\"inline-flex items-center rounded-full px-2 py-0.5 text-xs font-medium\"\n style={{ backgroundColor: `color-mix(in srgb, ${tint} 16%, transparent)`, color: tint }}\n >\n {formatEnumLabel(group)}\n </span>\n );\n};\n\n/** Token-safe generic enum <Select>. */\nfunction EnumSelect<T extends string>({\n id,\n value,\n onChange,\n options,\n}: {\n id?: string;\n value: T;\n onChange: (value: T) => void;\n options: readonly T[];\n}) {\n return (\n <Select value={value} onValueChange={(v) => onChange(v as T)}>\n <SelectTrigger id={id}>\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {options.map((opt) => (\n <SelectItem key={opt} value={opt}>\n {formatEnumLabel(opt)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n );\n}\n\ninterface WorkoutForm {\n name: string;\n type: MybWorkoutType;\n weekNumber: string;\n dayOfWeek: string;\n durationMin: string;\n}\n\nconst EMPTY_WORKOUT: WorkoutForm = {\n name: '',\n type: 'STRENGTH',\n weekNumber: '1',\n dayOfWeek: '1',\n durationMin: '',\n};\n\ninterface ExerciseForm {\n name: string;\n muscleGroup: MybMuscleGroup;\n sets: string;\n reps: string;\n targetWeight: string;\n restSeconds: string;\n instructions: string;\n}\n\nconst EMPTY_EXERCISE: ExerciseForm = {\n name: '',\n muscleGroup: 'FULL_BODY',\n sets: '',\n reps: '',\n targetWeight: '',\n restSeconds: '',\n instructions: '',\n};\n\nconst GOAL_TYPES: readonly MybGoalType[] = [\n 'WEIGHT_LOSS',\n 'MUSCLE_GAIN',\n 'STRENGTH',\n 'ENDURANCE',\n 'MOBILITY',\n 'GENERAL_FITNESS',\n];\nconst LEVELS: readonly MybFitnessLevel[] = ['BEGINNER', 'INTERMEDIATE', 'ADVANCED', 'ELITE'];\nconst PHASES: readonly MybProgramPhase[] = ['BUILD', 'PEAK', 'DELOAD', 'MAINTENANCE'];\n\ninterface ProgramEditForm {\n name: string;\n description: string;\n goalType: MybGoalType;\n level: MybFitnessLevel;\n phase: MybProgramPhase;\n weeks: string;\n daysPerWeek: string;\n startDate: string;\n endDate: string;\n}\n\nconst EMPTY_EDIT: ProgramEditForm = {\n name: '',\n description: '',\n goalType: 'GENERAL_FITNESS',\n level: 'BEGINNER',\n phase: 'BUILD',\n weeks: '',\n daysPerWeek: '',\n startDate: '',\n endDate: '',\n};\n\n/** ISO datetime → `YYYY-MM-DD` for a native `<input type=\"date\">`. */\nconst toDateInput = (iso: string | null | undefined): string => {\n if (!iso) return '';\n const d = new Date(iso);\n if (Number.isNaN(d.getTime())) return '';\n return d.toISOString().slice(0, 10);\n};\n\nfunction ExerciseRow({ exercise }: { exercise: MybExercise }) {\n const tint = MUSCLE_META[exercise.muscleGroup].tint;\n return (\n <div\n className=\"flex flex-col gap-2 rounded-lg border-l-2 py-3 pl-3 pr-1 transition-colors hover:bg-bg-sunken/40 sm:flex-row sm:items-start sm:justify-between\"\n style={{ borderColor: `color-mix(in srgb, ${tint} 55%, transparent)` }}\n >\n <div className=\"min-w-0\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <p className=\"font-medium text-text-primary\">{exercise.name}</p>\n <MuscleChip group={exercise.muscleGroup} />\n </div>\n {exercise.instructions && (\n <p className=\"mt-1 text-xs text-text-tertiary\">{exercise.instructions}</p>\n )}\n {exercise.demo?.id && (\n <div className=\"mt-1.5\">\n <FileLink fileId={exercise.demo.id} />\n </div>\n )}\n </div>\n <div className=\"flex shrink-0 items-center gap-4 text-sm tabular-nums text-text-secondary\">\n <span>\n {formatNumber(exercise.sets)}×{formatNumber(exercise.reps)} @{' '}\n {formatWeight(exercise.targetWeight)}\n </span>\n <span className=\"text-text-tertiary\">Rest {formatDuration(exercise.restSeconds)}</span>\n </div>\n </div>\n );\n}\n\nfunction WorkoutCard({\n workout,\n canEdit,\n onAddExercise,\n}: {\n workout: MybWorkout;\n canEdit: boolean;\n onAddExercise: (workoutId: string) => void;\n}) {\n const meta = WORKOUT_TYPE_META[workout.type];\n const TypeIcon = meta.icon;\n const exercises = useMemo(\n () => [...(workout.exercises ?? [])].sort((a, b) => a.orderIndex - b.orderIndex),\n [workout.exercises]\n );\n\n return (\n <GlassCard treatment=\"glass\" className=\"h-full p-5 transition-shadow hover:shadow-float\">\n <div className=\"flex flex-wrap items-start justify-between gap-3\">\n <div className=\"flex min-w-0 items-start gap-3\">\n <IconChip icon={<TypeIcon className=\"h-4 w-4\" />} tint={meta.tint} />\n <div className=\"min-w-0\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <h3 className=\"font-semibold text-text-primary\">{workout.name}</h3>\n <StatusBadge status={workout.type} />\n </div>\n <div className=\"mt-0.5 flex flex-wrap items-center gap-x-2 gap-y-0.5 text-xs text-text-tertiary\">\n <span>\n Week {formatNumber(workout.weekNumber)} · Day {formatNumber(workout.dayOfWeek)}\n </span>\n <span className=\"inline-flex items-center gap-1\">\n <Clock className=\"h-3.5 w-3.5\" />\n {formatMinutes(workout.durationMin)}\n </span>\n </div>\n </div>\n </div>\n {canEdit && (\n <Button variant=\"outline\" size=\"sm\" onClick={() => onAddExercise(workout.id)}>\n <Plus className=\"mr-1 h-4 w-4\" /> Add exercise\n </Button>\n )}\n </div>\n\n {exercises.length === 0 ? (\n <p className=\"mt-4 rounded-lg border border-dashed border-border-seam px-3 py-4 text-center text-sm text-text-tertiary\">\n No exercises yet — add the first movement to this workout.\n </p>\n ) : (\n <div className=\"mt-4 divide-y divide-border-seam/60\">\n {exercises.map((exercise) => (\n <ExerciseRow key={exercise.id} exercise={exercise} />\n ))}\n </div>\n )}\n </GlassCard>\n );\n}\n\nexport function ProgramDetailPage() {\n const { id } = useParams<{ id: string }>();\n const { navigateTo, workspaceId } = useMyBodyShield();\n const reduce = useReducedMotion();\n const analytics = useMyBodyShieldAnalytics();\n\n const { data: program, isLoading, isError, refetch } = useMybProgram(id);\n\n const transition = useMybTransitionProgram();\n const archiveM = useMybArchiveProgram();\n const updateProgram = useMybUpdateProgram();\n const addWorkout = useMybAddWorkout();\n const addExercise = useMybAddExercise();\n\n const [actionErr, setActionErr] = useState<string | null>(null);\n\n // Add-workout dialog\n const [workoutOpen, setWorkoutOpen] = useState(false);\n const [workoutForm, setWorkoutForm] = useState<WorkoutForm>(EMPTY_WORKOUT);\n const [workoutErr, setWorkoutErr] = useState<string | null>(null);\n\n // Add-exercise dialog (targets a specific workout)\n const [exerciseTarget, setExerciseTarget] = useState<string | null>(null);\n const [exerciseForm, setExerciseForm] = useState<ExerciseForm>(EMPTY_EXERCISE);\n const [exerciseDemo, setExerciseDemo] = useState<UploadedMybFile | null>(null);\n const [exerciseErr, setExerciseErr] = useState<string | null>(null);\n\n // Edit-program dialog\n const [editOpen, setEditOpen] = useState(false);\n const [editForm, setEditForm] = useState<ProgramEditForm>(EMPTY_EDIT);\n const [editErr, setEditErr] = useState<string | null>(null);\n\n const workouts = useMemo(\n () =>\n [...(program?.workouts ?? [])].sort(\n (a, b) =>\n a.weekNumber - b.weekNumber || a.dayOfWeek - b.dayOfWeek || a.orderIndex - b.orderIndex\n ),\n [program?.workouts]\n );\n\n if (isLoading) {\n return (\n <PageLayout title=\"Program\">\n <LoadingView label=\"Loading program…\" />\n </PageLayout>\n );\n }\n\n if (isError) {\n return (\n <PageLayout title=\"Program\">\n <ErrorView message=\"We couldn't load this program.\" onRetry={() => void refetch()} />\n </PageLayout>\n );\n }\n\n if (!program) {\n return (\n <PageLayout title=\"Program\">\n <EmptyState\n illustration=\"empty-data\"\n icon={<Dumbbell className=\"h-10 w-10\" />}\n title=\"Program not found\"\n description=\"This program may have been deleted or you don't have access to it.\"\n action={\n <Button variant=\"outline\" onClick={() => navigateTo('programs')}>\n <ArrowLeft className=\"mr-1 h-4 w-4\" /> Back to programs\n </Button>\n }\n />\n </PageLayout>\n );\n }\n\n const programId = program.id;\n // You only structure DRAFT/ACTIVE programs — COMPLETED/ARCHIVED are locked.\n const canStructure = program.status === 'DRAFT' || program.status === 'ACTIVE';\n const isActive = program.status === 'ACTIVE';\n const goalMeta = GOAL_TYPE_META[program.goalType];\n const GoalIcon = goalMeta.icon;\n\n const openEdit = () => {\n setEditForm({\n name: program.name,\n description: program.description ?? '',\n goalType: program.goalType,\n level: program.level,\n phase: program.phase,\n weeks: program.weeks ? String(program.weeks) : '',\n daysPerWeek: program.daysPerWeek ? String(program.daysPerWeek) : '',\n startDate: toDateInput(program.startDate),\n endDate: toDateInput(program.endDate),\n });\n setEditErr(null);\n setEditOpen(true);\n analytics.emitIntent('program', 'update', { workspaceId, entityId: programId });\n };\n\n const submitEdit = async () => {\n if (!editForm.name.trim()) {\n setEditErr('Program name is required.');\n return;\n }\n const input: MybUpdateProgramInput = {\n name: editForm.name.trim(),\n description: editForm.description.trim() || null,\n goalType: editForm.goalType,\n level: editForm.level,\n phase: editForm.phase,\n weeks: toNum(editForm.weeks),\n daysPerWeek: toNum(editForm.daysPerWeek),\n startDate: editForm.startDate ? new Date(editForm.startDate).toISOString() : null,\n endDate: editForm.endDate ? new Date(editForm.endDate).toISOString() : null,\n };\n setEditErr(null);\n try {\n await updateProgram.mutateAsync({ id: programId, input });\n analytics.emitFact('program', 'updated', { workspaceId, entityId: programId });\n setEditOpen(false);\n } catch (e) {\n setEditErr(e instanceof Error ? e.message : 'Failed to update program.');\n }\n };\n\n const doTransition = async (status: MybProgramStatus) => {\n setActionErr(null);\n try {\n await transition.mutateAsync({ id: programId, status });\n analytics.emitFact('program', 'status_changed', { workspaceId, entityId: programId });\n } catch (e) {\n setActionErr(e instanceof Error ? e.message : 'Failed to update program status.');\n }\n };\n\n const doArchive = async () => {\n if (!window.confirm('Archive this program? It will be moved out of your active library.')) {\n return;\n }\n setActionErr(null);\n try {\n await archiveM.mutateAsync(programId);\n analytics.emitFact('program', 'archived', { workspaceId, entityId: programId });\n await refetch();\n } catch (e) {\n setActionErr(e instanceof Error ? e.message : 'Failed to archive program.');\n }\n };\n\n const openAddWorkout = () => {\n setWorkoutForm(EMPTY_WORKOUT);\n setWorkoutErr(null);\n setWorkoutOpen(true);\n analytics.emitIntent('workout', 'create', { workspaceId, entityId: programId });\n };\n\n const submitWorkout = async () => {\n if (!workoutForm.name.trim()) {\n setWorkoutErr('Workout name is required.');\n return;\n }\n const input: MybCreateWorkoutInput = {\n programId,\n name: workoutForm.name.trim(),\n type: workoutForm.type,\n weekNumber: toNum(workoutForm.weekNumber),\n dayOfWeek: toNum(workoutForm.dayOfWeek),\n durationMin: toNum(workoutForm.durationMin),\n };\n try {\n const created = await addWorkout.mutateAsync(input);\n analytics.emitFact('workout', 'created', { workspaceId, entityId: created.id });\n setWorkoutOpen(false);\n } catch (e) {\n setWorkoutErr(e instanceof Error ? e.message : 'Failed to add workout.');\n }\n };\n\n const openAddExercise = (workoutId: string) => {\n setExerciseForm(EMPTY_EXERCISE);\n setExerciseDemo(null);\n setExerciseErr(null);\n setExerciseTarget(workoutId);\n analytics.emitIntent('exercise', 'create', { workspaceId, entityId: workoutId });\n };\n\n const submitExercise = async () => {\n if (!exerciseTarget) return;\n if (!exerciseForm.name.trim()) {\n setExerciseErr('Exercise name is required.');\n return;\n }\n const input: MybCreateExerciseInput = {\n workoutId: exerciseTarget,\n name: exerciseForm.name.trim(),\n muscleGroup: exerciseForm.muscleGroup,\n sets: toNum(exerciseForm.sets),\n reps: toNum(exerciseForm.reps),\n targetWeight: toNum(exerciseForm.targetWeight),\n restSeconds: toNum(exerciseForm.restSeconds),\n instructions: exerciseForm.instructions.trim() || null,\n demoFileId: exerciseDemo?.fileId ?? null,\n };\n try {\n const created = await addExercise.mutateAsync(input);\n analytics.emitFact('exercise', 'created', { workspaceId, entityId: created.id });\n setExerciseTarget(null);\n } catch (e) {\n setExerciseErr(e instanceof Error ? e.message : 'Failed to add exercise.');\n }\n };\n\n return (\n <PageLayout\n title={program.name}\n actions={\n <>\n {program.status === 'DRAFT' && (\n <Button onClick={() => void doTransition('ACTIVE')} disabled={transition.isPending}>\n <Play className=\"mr-1 h-4 w-4\" /> Activate\n </Button>\n )}\n {program.status === 'ACTIVE' && (\n <>\n <Button\n onClick={() => void doTransition('COMPLETED')}\n disabled={transition.isPending}\n >\n <CheckCircle2 className=\"mr-1 h-4 w-4\" /> Complete\n </Button>\n <Button variant=\"outline\" onClick={() => navigateTo('train')}>\n <Flame className=\"mr-1 h-4 w-4\" /> Start a session\n </Button>\n </>\n )}\n {program.status !== 'ARCHIVED' && (\n <Button variant=\"outline\" onClick={openEdit} disabled={updateProgram.isPending}>\n <Pencil className=\"mr-1 h-4 w-4\" /> Edit\n </Button>\n )}\n {program.status !== 'ARCHIVED' && (\n <Button\n variant=\"outline\"\n onClick={() => void doArchive()}\n disabled={archiveM.isPending}\n >\n <Archive className=\"mr-1 h-4 w-4\" /> Archive\n </Button>\n )}\n {canStructure && (\n <Button onClick={openAddWorkout}>\n <Plus className=\"mr-1 h-4 w-4\" /> Add workout\n </Button>\n )}\n </>\n }\n >\n <button\n type=\"button\"\n onClick={() => navigateTo('programs')}\n className=\"mb-4 inline-flex items-center gap-1 text-sm font-medium text-accent hover:underline\"\n >\n <ArrowLeft className=\"h-4 w-4\" /> All programs\n </button>\n\n {/* Header card */}\n <motion.div\n initial={reduce ? false : { opacity: 0, y: 10 }}\n animate={{ opacity: 1, y: 0 }}\n transition={reduce ? { duration: 0 } : { duration: 0.35, ease: MOTION_EASE }}\n >\n <GlassCard treatment=\"glass\" glow={isActive} className=\"p-6\">\n <div className=\"flex flex-wrap items-start justify-between gap-4\">\n <div className=\"min-w-0\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <IconChip\n icon={<GoalIcon className=\"h-5 w-5\" />}\n tint={goalMeta.tint}\n className=\"size-10\"\n />\n <h2 className=\"text-xl font-semibold text-text-primary\">{program.name}</h2>\n <StatusBadge status={program.status} />\n </div>\n <div className=\"mt-3 flex flex-wrap gap-1.5\">\n <Chip>Phase · {formatEnumLabel(program.phase)}</Chip>\n <Chip>Level · {formatEnumLabel(program.level)}</Chip>\n <Chip icon={<GoalIcon className=\"h-3.5 w-3.5\" />} tint={goalMeta.tint}>\n {formatEnumLabel(program.goalType)}\n </Chip>\n <Chip icon={<CalendarDays className=\"h-3.5 w-3.5\" />}>\n {formatNumber(program.weeks)} wk × {formatNumber(program.daysPerWeek)} days\n </Chip>\n </div>\n <p className=\"mt-3 inline-flex items-center gap-1.5 text-sm text-text-secondary\">\n <CalendarDays className=\"h-4 w-4 text-text-tertiary\" />\n {formatDate(program.startDate)} – {formatDate(program.endDate)}\n </p>\n {(program.tags ?? []).length > 0 && (\n <div className=\"mt-3 flex flex-wrap gap-1.5\">\n {(program.tags ?? []).map((tag) => (\n <span\n key={tag}\n className=\"inline-flex items-center rounded-full bg-[var(--color-accent-soft)] px-2 py-0.5 text-xs font-medium text-text-secondary\"\n >\n #{tag}\n </span>\n ))}\n </div>\n )}\n {program.description && (\n <p className=\"mt-3 max-w-2xl text-sm text-text-secondary\">{program.description}</p>\n )}\n </div>\n </div>\n\n {/* Editable platform tags (the inline #tags above are a read-only supplement). */}\n <div className=\"mt-5 border-t border-border-seam pt-4\">\n <MybTags\n entityType=\"mybodyshield-program\"\n entityId={program.id}\n label=\"Platform tags\"\n />\n </div>\n\n {actionErr && (\n <p className=\"mt-4 rounded-lg bg-status-error-bg-subtle px-3 py-2 text-sm text-status-error-text\">\n {actionErr}\n </p>\n )}\n </GlassCard>\n </motion.div>\n\n {/* Workouts */}\n <Section\n title=\"Workouts\"\n description=\"Sessions in this block, ordered by week, day & sequence.\"\n >\n {!canStructure && (\n <p className=\"mb-4 inline-flex items-center gap-1.5 rounded-lg border border-border-seam bg-bg-sunken/60 px-3 py-2 text-xs text-text-tertiary\">\n <Lock className=\"h-3.5 w-3.5 shrink-0\" />\n This program is {formatEnumLabel(program.status).toLowerCase()} — its structure is\n locked and can no longer be edited.\n </p>\n )}\n {workouts.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n icon={<Dumbbell className=\"h-10 w-10\" />}\n title=\"No workouts yet\"\n description={\n canStructure\n ? 'Add workouts to lay out the training week, then attach exercises to each one.'\n : 'This program has no workouts, and its structure is locked in its current status.'\n }\n action={\n canStructure ? (\n <Button onClick={openAddWorkout}>\n <Plus className=\"mr-1 h-4 w-4\" /> Add workout\n </Button>\n ) : undefined\n }\n />\n ) : (\n <motion.div\n className=\"grid grid-cols-1 gap-4 xl:grid-cols-2\"\n variants={reduce ? undefined : STAGGER_CONTAINER}\n initial={reduce ? false : 'hidden'}\n animate=\"show\"\n >\n {workouts.map((workout) => (\n <motion.div\n key={workout.id}\n className=\"h-full\"\n variants={reduce ? undefined : RISE_ITEM}\n >\n <WorkoutCard\n workout={workout}\n canEdit={canStructure}\n onAddExercise={openAddExercise}\n />\n </motion.div>\n ))}\n </motion.div>\n )}\n </Section>\n\n {/* Coach & member notes */}\n <MybDiscussion\n entityType=\"mybodyshield-program\"\n entityId={program.id}\n title=\"Program notes\"\n description=\"Coach and member notes for this training block.\"\n />\n\n {/* Add-workout dialog */}\n <ResponsiveDialog open={workoutOpen} onOpenChange={setWorkoutOpen}>\n <ResponsiveDialogContent className=\"sm:max-w-lg\">\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>Add workout</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n Add a training session to this program. Attach exercises once it's created.\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n\n <div className=\"space-y-4 py-2\">\n {workoutErr && (\n <p className=\"rounded-lg bg-status-error-bg-subtle px-3 py-2 text-sm text-status-error-text\">\n {workoutErr}\n </p>\n )}\n\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-wk-name\">Name</Label>\n <Input\n id=\"myb-wk-name\"\n value={workoutForm.name}\n onChange={(e) => setWorkoutForm({ ...workoutForm, name: e.target.value })}\n placeholder=\"e.g. Upper Body — Push\"\n />\n </div>\n\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-wk-type\">Type</Label>\n <EnumSelect\n id=\"myb-wk-type\"\n value={workoutForm.type}\n onChange={(v) => setWorkoutForm({ ...workoutForm, type: v })}\n options={WORKOUT_TYPES}\n />\n </div>\n\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-3\">\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-wk-week\">Week</Label>\n <Input\n id=\"myb-wk-week\"\n type=\"number\"\n min={1}\n value={workoutForm.weekNumber}\n onChange={(e) => setWorkoutForm({ ...workoutForm, weekNumber: e.target.value })}\n placeholder=\"1\"\n />\n </div>\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-wk-day\">Day</Label>\n <Input\n id=\"myb-wk-day\"\n type=\"number\"\n min={1}\n max={7}\n value={workoutForm.dayOfWeek}\n onChange={(e) => setWorkoutForm({ ...workoutForm, dayOfWeek: e.target.value })}\n placeholder=\"1\"\n />\n </div>\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-wk-dur\">Duration (min)</Label>\n <Input\n id=\"myb-wk-dur\"\n type=\"number\"\n min={0}\n value={workoutForm.durationMin}\n onChange={(e) => setWorkoutForm({ ...workoutForm, durationMin: e.target.value })}\n placeholder=\"60\"\n />\n </div>\n </div>\n </div>\n\n <ResponsiveDialogFooter>\n <Button\n variant=\"outline\"\n onClick={() => setWorkoutOpen(false)}\n disabled={addWorkout.isPending}\n >\n Cancel\n </Button>\n <Button onClick={() => void submitWorkout()} disabled={addWorkout.isPending}>\n {addWorkout.isPending ? 'Adding…' : 'Add workout'}\n </Button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n\n {/* Add-exercise dialog */}\n <ResponsiveDialog\n open={exerciseTarget !== null}\n onOpenChange={(open) => {\n if (!open) setExerciseTarget(null);\n }}\n >\n <ResponsiveDialogContent className=\"sm:max-w-lg\">\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>Add exercise</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n Add a movement with its target sets, reps, load & rest.\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n\n <div className=\"space-y-4 py-2\">\n {exerciseErr && (\n <p className=\"rounded-lg bg-status-error-bg-subtle px-3 py-2 text-sm text-status-error-text\">\n {exerciseErr}\n </p>\n )}\n\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-ex-name\">Name</Label>\n <Input\n id=\"myb-ex-name\"\n value={exerciseForm.name}\n onChange={(e) => setExerciseForm({ ...exerciseForm, name: e.target.value })}\n placeholder=\"e.g. Barbell Bench Press\"\n />\n </div>\n\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-ex-muscle\">Muscle group</Label>\n <EnumSelect\n id=\"myb-ex-muscle\"\n value={exerciseForm.muscleGroup}\n onChange={(v) => setExerciseForm({ ...exerciseForm, muscleGroup: v })}\n options={MUSCLE_GROUPS}\n />\n </div>\n\n <div className=\"grid grid-cols-2 gap-4 sm:grid-cols-4\">\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-ex-sets\">Sets</Label>\n <Input\n id=\"myb-ex-sets\"\n type=\"number\"\n min={0}\n value={exerciseForm.sets}\n onChange={(e) => setExerciseForm({ ...exerciseForm, sets: e.target.value })}\n placeholder=\"3\"\n />\n </div>\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-ex-reps\">Reps</Label>\n <Input\n id=\"myb-ex-reps\"\n type=\"number\"\n min={0}\n value={exerciseForm.reps}\n onChange={(e) => setExerciseForm({ ...exerciseForm, reps: e.target.value })}\n placeholder=\"10\"\n />\n </div>\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-ex-weight\">Weight (kg)</Label>\n <Input\n id=\"myb-ex-weight\"\n type=\"number\"\n min={0}\n step=\"0.5\"\n value={exerciseForm.targetWeight}\n onChange={(e) =>\n setExerciseForm({ ...exerciseForm, targetWeight: e.target.value })\n }\n placeholder=\"60\"\n />\n </div>\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-ex-rest\">Rest (sec)</Label>\n <Input\n id=\"myb-ex-rest\"\n type=\"number\"\n min={0}\n value={exerciseForm.restSeconds}\n onChange={(e) =>\n setExerciseForm({ ...exerciseForm, restSeconds: e.target.value })\n }\n placeholder=\"90\"\n />\n </div>\n </div>\n\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-ex-instructions\">Instructions</Label>\n <Textarea\n id=\"myb-ex-instructions\"\n rows={3}\n value={exerciseForm.instructions}\n onChange={(e) => setExerciseForm({ ...exerciseForm, instructions: e.target.value })}\n placeholder=\"Cues, tempo, form notes…\"\n />\n </div>\n\n <EvidenceUpload\n id=\"myb-ex-demo\"\n label=\"Demo media (optional)\"\n value={exerciseDemo}\n onChange={setExerciseDemo}\n accept=\"video/*,image/*\"\n disabled={addExercise.isPending}\n hint=\"Attach a short demo clip or image to show how the movement is performed.\"\n />\n </div>\n\n <ResponsiveDialogFooter>\n <Button\n variant=\"outline\"\n onClick={() => setExerciseTarget(null)}\n disabled={addExercise.isPending}\n >\n Cancel\n </Button>\n <Button onClick={() => void submitExercise()} disabled={addExercise.isPending}>\n {addExercise.isPending ? 'Adding…' : 'Add exercise'}\n </Button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n\n {/* Edit-program dialog */}\n <ResponsiveDialog open={editOpen} onOpenChange={setEditOpen}>\n <ResponsiveDialogContent className=\"sm:max-w-lg\">\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>Edit program</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n Update this program's plan, goal & schedule. Workouts stay untouched.\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n\n <div className=\"space-y-4 py-2\">\n {editErr && (\n <p className=\"rounded-lg bg-status-error-bg-subtle px-3 py-2 text-sm text-status-error-text\">\n {editErr}\n </p>\n )}\n\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-edit-name\">Name</Label>\n <Input\n id=\"myb-edit-name\"\n value={editForm.name}\n onChange={(e) => setEditForm({ ...editForm, name: e.target.value })}\n placeholder=\"e.g. Hypertrophy Block A\"\n />\n </div>\n\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-edit-desc\">Description</Label>\n <Textarea\n id=\"myb-edit-desc\"\n rows={3}\n value={editForm.description}\n onChange={(e) => setEditForm({ ...editForm, description: e.target.value })}\n placeholder=\"Focus, progression scheme, notes…\"\n />\n </div>\n\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-3\">\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-edit-goal\">Goal type</Label>\n <EnumSelect\n id=\"myb-edit-goal\"\n value={editForm.goalType}\n onChange={(v) => setEditForm({ ...editForm, goalType: v })}\n options={GOAL_TYPES}\n />\n </div>\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-edit-level\">Level</Label>\n <EnumSelect\n id=\"myb-edit-level\"\n value={editForm.level}\n onChange={(v) => setEditForm({ ...editForm, level: v })}\n options={LEVELS}\n />\n </div>\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-edit-phase\">Phase</Label>\n <EnumSelect\n id=\"myb-edit-phase\"\n value={editForm.phase}\n onChange={(v) => setEditForm({ ...editForm, phase: v })}\n options={PHASES}\n />\n </div>\n </div>\n\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2\">\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-edit-weeks\">Weeks</Label>\n <Input\n id=\"myb-edit-weeks\"\n type=\"number\"\n min={1}\n value={editForm.weeks}\n onChange={(e) => setEditForm({ ...editForm, weeks: e.target.value })}\n placeholder=\"8\"\n />\n </div>\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-edit-days\">Days / week</Label>\n <Input\n id=\"myb-edit-days\"\n type=\"number\"\n min={1}\n max={7}\n value={editForm.daysPerWeek}\n onChange={(e) => setEditForm({ ...editForm, daysPerWeek: e.target.value })}\n placeholder=\"4\"\n />\n </div>\n </div>\n\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2\">\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-edit-start\">Start date</Label>\n <Input\n id=\"myb-edit-start\"\n type=\"date\"\n value={editForm.startDate}\n onChange={(e) => setEditForm({ ...editForm, startDate: e.target.value })}\n />\n </div>\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-edit-end\">End date</Label>\n <Input\n id=\"myb-edit-end\"\n type=\"date\"\n value={editForm.endDate}\n onChange={(e) => setEditForm({ ...editForm, endDate: e.target.value })}\n />\n </div>\n </div>\n </div>\n\n <ResponsiveDialogFooter>\n <Button\n variant=\"outline\"\n onClick={() => setEditOpen(false)}\n disabled={updateProgram.isPending}\n >\n Cancel\n </Button>\n <Button onClick={() => void submitEdit()} disabled={updateProgram.isPending}>\n {updateProgram.isPending ? 'Saving…' : 'Save changes'}\n </Button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ProgramDetailPage.js","names":[],"sources":["../../../../src/mybodyshield/pages/programs/ProgramDetailPage.tsx"],"sourcesContent":["/**\n * Program detail — a single training block with its workouts & exercises.\n * @module mybodyshield/pages/programs\n *\n * Renders one `mybProgram` (with its nested `workouts[].exercises[]`), lifecycle\n * transition controls (activate / complete / archive) and dialogs to add\n * workouts and exercises. All data & mutations hit the real `myb*` contract.\n */\n\nimport { useMemo, useState, type FC, type ReactNode } from 'react';\nimport { motion, useReducedMotion } from 'framer-motion';\nimport { useParams } from 'react-router';\nimport {\n ArrowLeft,\n Plus,\n CheckCircle2,\n Archive,\n Play,\n Dumbbell,\n Pencil,\n Flame,\n Lock,\n Clock,\n CalendarDays,\n} from 'lucide-react';\nimport {\n Button,\n Input,\n Label,\n Textarea,\n Select,\n SelectTrigger,\n SelectContent,\n SelectItem,\n SelectValue,\n GlassCard,\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n ResponsiveDialogDescription,\n ResponsiveDialogFooter,\n} from '@burdenoff/fe-libs/ui';\nimport {\n PageLayout,\n StatusBadge,\n EmptyState,\n LoadingView,\n ErrorView,\n Section,\n EvidenceUpload,\n FileLink,\n MybDiscussion,\n MybTags,\n} from '../../components';\nimport { IconChip, WORKOUT_TYPE_META, MUSCLE_META, GOAL_TYPE_META } from '../_shared/visuals';\nimport { useMyBodyShield } from '../../context';\nimport {\n useMybProgram,\n useMybUpdateProgram,\n useMybAddWorkout,\n useMybAddExercise,\n useMybTransitionProgram,\n useMybArchiveProgram,\n useMyBodyShieldAnalytics,\n type UploadedMybFile,\n} from '../../hooks';\nimport {\n formatEnumLabel,\n formatDate,\n formatNumber,\n formatWeight,\n formatDuration,\n formatMinutes,\n} from '../../utils/formatters';\nimport type {\n MybWorkout,\n MybExercise,\n MybWorkoutType,\n MybMuscleGroup,\n MybProgramStatus,\n MybGoalType,\n MybFitnessLevel,\n MybProgramPhase,\n MybCreateWorkoutInput,\n MybCreateExerciseInput,\n MybUpdateProgramInput,\n} from '../../types';\n\nconst WORKOUT_TYPES: readonly MybWorkoutType[] = [\n 'STRENGTH',\n 'CARDIO',\n 'HIIT',\n 'MOBILITY',\n 'YOGA',\n 'RECOVERY',\n];\nconst MUSCLE_GROUPS: readonly MybMuscleGroup[] = [\n 'CHEST',\n 'BACK',\n 'LEGS',\n 'SHOULDERS',\n 'ARMS',\n 'CORE',\n 'FULL_BODY',\n];\n\n/** Parse a numeric text field → number | null (null when blank / invalid). */\nconst toNum = (raw: string): number | null => {\n const t = raw.trim();\n if (t === '') return null;\n const n = Number(t);\n return Number.isFinite(n) ? n : null;\n};\n\n/** Shared entrance easing — a soft, decelerating cubic-bezier. */\nconst MOTION_EASE: [number, number, number, number] = [0.16, 1, 0.3, 1];\n\nconst STAGGER_CONTAINER = {\n hidden: {},\n show: { transition: { staggerChildren: 0.06 } },\n};\nconst RISE_ITEM = {\n hidden: { opacity: 0, y: 12 },\n show: { opacity: 1, y: 0, transition: { duration: 0.35, ease: MOTION_EASE } },\n};\n\nconst Chip: FC<{ children: ReactNode; icon?: ReactNode; tint?: string }> = ({\n children,\n icon,\n tint,\n}) => (\n <span\n className=\"inline-flex items-center gap-1.5 rounded-full bg-bg-sunken px-2.5 py-1 text-xs font-medium text-text-secondary\"\n style={tint ? { color: tint } : undefined}\n >\n {icon}\n {children}\n </span>\n);\n\nconst MuscleChip: FC<{ group: MybMuscleGroup }> = ({ group }) => {\n const tint = MUSCLE_META[group].tint;\n return (\n <span\n className=\"inline-flex items-center rounded-full px-2 py-0.5 text-xs font-medium\"\n style={{ backgroundColor: `color-mix(in srgb, ${tint} 16%, transparent)`, color: tint }}\n >\n {formatEnumLabel(group)}\n </span>\n );\n};\n\n/** Token-safe generic enum <Select>. */\nfunction EnumSelect<T extends string>({\n id,\n value,\n onChange,\n options,\n}: {\n id?: string;\n value: T;\n onChange: (value: T) => void;\n options: readonly T[];\n}) {\n return (\n <Select value={value} onValueChange={(v) => onChange(v as T)}>\n <SelectTrigger id={id}>\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {options.map((opt) => (\n <SelectItem key={opt} value={opt}>\n {formatEnumLabel(opt)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n );\n}\n\ninterface WorkoutForm {\n name: string;\n type: MybWorkoutType;\n weekNumber: string;\n dayOfWeek: string;\n durationMin: string;\n}\n\nconst EMPTY_WORKOUT: WorkoutForm = {\n name: '',\n type: 'STRENGTH',\n weekNumber: '1',\n dayOfWeek: '1',\n durationMin: '',\n};\n\ninterface ExerciseForm {\n name: string;\n muscleGroup: MybMuscleGroup;\n sets: string;\n reps: string;\n targetWeight: string;\n restSeconds: string;\n instructions: string;\n}\n\nconst EMPTY_EXERCISE: ExerciseForm = {\n name: '',\n muscleGroup: 'FULL_BODY',\n sets: '',\n reps: '',\n targetWeight: '',\n restSeconds: '',\n instructions: '',\n};\n\nconst GOAL_TYPES: readonly MybGoalType[] = [\n 'WEIGHT_LOSS',\n 'MUSCLE_GAIN',\n 'STRENGTH',\n 'ENDURANCE',\n 'MOBILITY',\n 'GENERAL_FITNESS',\n];\nconst LEVELS: readonly MybFitnessLevel[] = ['BEGINNER', 'INTERMEDIATE', 'ADVANCED', 'ELITE'];\nconst PHASES: readonly MybProgramPhase[] = ['BUILD', 'PEAK', 'DELOAD', 'MAINTENANCE'];\n\ninterface ProgramEditForm {\n name: string;\n description: string;\n goalType: MybGoalType;\n level: MybFitnessLevel;\n phase: MybProgramPhase;\n weeks: string;\n daysPerWeek: string;\n startDate: string;\n endDate: string;\n}\n\nconst EMPTY_EDIT: ProgramEditForm = {\n name: '',\n description: '',\n goalType: 'GENERAL_FITNESS',\n level: 'BEGINNER',\n phase: 'BUILD',\n weeks: '',\n daysPerWeek: '',\n startDate: '',\n endDate: '',\n};\n\n/** ISO datetime → `YYYY-MM-DD` for a native `<input type=\"date\">`. */\nconst toDateInput = (iso: string | null | undefined): string => {\n if (!iso) return '';\n const d = new Date(iso);\n if (Number.isNaN(d.getTime())) return '';\n return d.toISOString().slice(0, 10);\n};\n\nfunction ExerciseRow({ exercise }: { exercise: MybExercise }) {\n const tint = MUSCLE_META[exercise.muscleGroup].tint;\n return (\n <div\n className=\"flex flex-col gap-2 rounded-lg border-l-2 py-3 pl-3 pr-1 transition-colors hover:bg-bg-sunken/40 sm:flex-row sm:items-start sm:justify-between\"\n style={{ borderColor: `color-mix(in srgb, ${tint} 55%, transparent)` }}\n >\n <div className=\"min-w-0\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <p className=\"font-medium text-text-primary\">{exercise.name}</p>\n <MuscleChip group={exercise.muscleGroup} />\n </div>\n {exercise.instructions && (\n <p className=\"mt-1 text-xs text-text-tertiary\">{exercise.instructions}</p>\n )}\n {exercise.demo?.id && (\n <div className=\"mt-1.5\">\n <FileLink fileId={exercise.demo.id} />\n </div>\n )}\n </div>\n <div className=\"flex shrink-0 items-center gap-4 text-sm tabular-nums text-text-secondary\">\n <span>\n {formatNumber(exercise.sets)}×{formatNumber(exercise.reps)} @{' '}\n {formatWeight(exercise.targetWeight)}\n </span>\n <span className=\"text-text-tertiary\">Rest {formatDuration(exercise.restSeconds)}</span>\n </div>\n </div>\n );\n}\n\nfunction WorkoutCard({\n workout,\n canEdit,\n onAddExercise,\n}: {\n workout: MybWorkout;\n canEdit: boolean;\n onAddExercise: (workoutId: string) => void;\n}) {\n const meta = WORKOUT_TYPE_META[workout.type];\n const TypeIcon = meta.icon;\n const exercises = useMemo(\n () => [...(workout.exercises ?? [])].sort((a, b) => a.orderIndex - b.orderIndex),\n [workout.exercises]\n );\n\n return (\n <GlassCard treatment=\"glass\" className=\"h-full p-5 transition-shadow hover:shadow-float\">\n <div className=\"flex flex-wrap items-start justify-between gap-3\">\n <div className=\"flex min-w-0 items-start gap-3\">\n <IconChip icon={<TypeIcon className=\"h-4 w-4\" />} tint={meta.tint} />\n <div className=\"min-w-0\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <h3 className=\"font-semibold text-text-primary\">{workout.name}</h3>\n <StatusBadge status={workout.type} />\n </div>\n <div className=\"mt-0.5 flex flex-wrap items-center gap-x-2 gap-y-0.5 text-xs text-text-tertiary\">\n <span>\n Week {formatNumber(workout.weekNumber)} · Day {formatNumber(workout.dayOfWeek)}\n </span>\n <span className=\"inline-flex items-center gap-1\">\n <Clock className=\"h-3.5 w-3.5\" />\n {formatMinutes(workout.durationMin)}\n </span>\n </div>\n </div>\n </div>\n {canEdit && (\n <Button variant=\"outline\" size=\"sm\" onClick={() => onAddExercise(workout.id)}>\n <Plus className=\"mr-1 h-4 w-4\" /> Add exercise\n </Button>\n )}\n </div>\n\n {exercises.length === 0 ? (\n <p className=\"mt-4 rounded-lg border border-dashed border-border-seam px-3 py-4 text-center text-sm text-text-tertiary\">\n No exercises yet — add the first movement to this workout.\n </p>\n ) : (\n <div className=\"mt-4 divide-y divide-border-seam/60\">\n {exercises.map((exercise) => (\n <ExerciseRow key={exercise.id} exercise={exercise} />\n ))}\n </div>\n )}\n </GlassCard>\n );\n}\n\nexport function ProgramDetailPage() {\n const { id } = useParams<{ id: string }>();\n const { navigateTo, workspaceId } = useMyBodyShield();\n const reduce = useReducedMotion();\n const analytics = useMyBodyShieldAnalytics();\n\n const { data: program, isLoading, isError, refetch } = useMybProgram(id);\n\n const transition = useMybTransitionProgram();\n const archiveM = useMybArchiveProgram();\n const updateProgram = useMybUpdateProgram();\n const addWorkout = useMybAddWorkout();\n const addExercise = useMybAddExercise();\n\n const [actionErr, setActionErr] = useState<string | null>(null);\n\n // Add-workout dialog\n const [workoutOpen, setWorkoutOpen] = useState(false);\n const [workoutForm, setWorkoutForm] = useState<WorkoutForm>(EMPTY_WORKOUT);\n const [workoutErr, setWorkoutErr] = useState<string | null>(null);\n\n // Add-exercise dialog (targets a specific workout)\n const [exerciseTarget, setExerciseTarget] = useState<string | null>(null);\n const [exerciseForm, setExerciseForm] = useState<ExerciseForm>(EMPTY_EXERCISE);\n const [exerciseDemo, setExerciseDemo] = useState<UploadedMybFile | null>(null);\n const [exerciseErr, setExerciseErr] = useState<string | null>(null);\n\n // Edit-program dialog\n const [editOpen, setEditOpen] = useState(false);\n const [editForm, setEditForm] = useState<ProgramEditForm>(EMPTY_EDIT);\n const [editErr, setEditErr] = useState<string | null>(null);\n\n const workouts = useMemo(\n () =>\n [...(program?.workouts ?? [])].sort(\n (a, b) =>\n a.weekNumber - b.weekNumber || a.dayOfWeek - b.dayOfWeek || a.orderIndex - b.orderIndex\n ),\n [program?.workouts]\n );\n\n if (isLoading) {\n return (\n <PageLayout title=\"Program\">\n <LoadingView label=\"Loading program…\" />\n </PageLayout>\n );\n }\n\n if (isError) {\n return (\n <PageLayout title=\"Program\">\n <ErrorView message=\"We couldn't load this program.\" onRetry={() => void refetch()} />\n </PageLayout>\n );\n }\n\n if (!program) {\n return (\n <PageLayout title=\"Program\">\n <EmptyState\n illustration=\"empty-data\"\n icon={<Dumbbell className=\"h-10 w-10\" />}\n title=\"Program not found\"\n description=\"This program may have been deleted or you don't have access to it.\"\n action={\n <Button variant=\"outline\" onClick={() => navigateTo('programs')}>\n <ArrowLeft className=\"mr-1 h-4 w-4\" /> Back to programs\n </Button>\n }\n />\n </PageLayout>\n );\n }\n\n const programId = program.id;\n // You only structure DRAFT/ACTIVE programs — COMPLETED/ARCHIVED are locked.\n const canStructure = program.status === 'DRAFT' || program.status === 'ACTIVE';\n const isActive = program.status === 'ACTIVE';\n const goalMeta = GOAL_TYPE_META[program.goalType];\n const GoalIcon = goalMeta.icon;\n\n const openEdit = () => {\n setEditForm({\n name: program.name,\n description: program.description ?? '',\n goalType: program.goalType,\n level: program.level,\n phase: program.phase,\n weeks: program.weeks ? String(program.weeks) : '',\n daysPerWeek: program.daysPerWeek ? String(program.daysPerWeek) : '',\n startDate: toDateInput(program.startDate),\n endDate: toDateInput(program.endDate),\n });\n setEditErr(null);\n setEditOpen(true);\n analytics.emitIntent('program', 'update', { workspaceId, entityId: programId });\n };\n\n const submitEdit = async () => {\n if (!editForm.name.trim()) {\n setEditErr('Program name is required.');\n return;\n }\n const input: MybUpdateProgramInput = {\n name: editForm.name.trim(),\n description: editForm.description.trim() || null,\n goalType: editForm.goalType,\n level: editForm.level,\n phase: editForm.phase,\n weeks: toNum(editForm.weeks),\n daysPerWeek: toNum(editForm.daysPerWeek),\n startDate: editForm.startDate ? new Date(editForm.startDate).toISOString() : null,\n endDate: editForm.endDate ? new Date(editForm.endDate).toISOString() : null,\n };\n setEditErr(null);\n try {\n await updateProgram.mutateAsync({ id: programId, input });\n analytics.emitFact('program', 'updated', { workspaceId, entityId: programId });\n setEditOpen(false);\n } catch (e) {\n setEditErr(e instanceof Error ? e.message : 'Failed to update program.');\n }\n };\n\n const doTransition = async (status: MybProgramStatus) => {\n setActionErr(null);\n try {\n await transition.mutateAsync({ id: programId, status });\n analytics.emitFact('program', 'status_changed', { workspaceId, entityId: programId });\n } catch (e) {\n setActionErr(e instanceof Error ? e.message : 'Failed to update program status.');\n }\n };\n\n const doArchive = async () => {\n if (!window.confirm('Archive this program? It will be moved out of your active library.')) {\n return;\n }\n setActionErr(null);\n try {\n await archiveM.mutateAsync(programId);\n analytics.emitFact('program', 'archived', { workspaceId, entityId: programId });\n await refetch();\n } catch (e) {\n setActionErr(e instanceof Error ? e.message : 'Failed to archive program.');\n }\n };\n\n const openAddWorkout = () => {\n setWorkoutForm(EMPTY_WORKOUT);\n setWorkoutErr(null);\n setWorkoutOpen(true);\n analytics.emitIntent('workout', 'create', { workspaceId, entityId: programId });\n };\n\n const submitWorkout = async () => {\n if (!workoutForm.name.trim()) {\n setWorkoutErr('Workout name is required.');\n return;\n }\n const input: MybCreateWorkoutInput = {\n programId,\n name: workoutForm.name.trim(),\n type: workoutForm.type,\n weekNumber: toNum(workoutForm.weekNumber),\n dayOfWeek: toNum(workoutForm.dayOfWeek),\n durationMin: toNum(workoutForm.durationMin),\n };\n try {\n const created = await addWorkout.mutateAsync(input);\n analytics.emitFact('workout', 'created', { workspaceId, entityId: created.id });\n setWorkoutOpen(false);\n } catch (e) {\n setWorkoutErr(e instanceof Error ? e.message : 'Failed to add workout.');\n }\n };\n\n const openAddExercise = (workoutId: string) => {\n setExerciseForm(EMPTY_EXERCISE);\n setExerciseDemo(null);\n setExerciseErr(null);\n setExerciseTarget(workoutId);\n analytics.emitIntent('exercise', 'create', { workspaceId, entityId: workoutId });\n };\n\n const submitExercise = async () => {\n if (!exerciseTarget) return;\n if (!exerciseForm.name.trim()) {\n setExerciseErr('Exercise name is required.');\n return;\n }\n const input: MybCreateExerciseInput = {\n workoutId: exerciseTarget,\n name: exerciseForm.name.trim(),\n muscleGroup: exerciseForm.muscleGroup,\n sets: toNum(exerciseForm.sets),\n reps: toNum(exerciseForm.reps),\n targetWeight: toNum(exerciseForm.targetWeight),\n restSeconds: toNum(exerciseForm.restSeconds),\n instructions: exerciseForm.instructions.trim() || null,\n demoFileId: exerciseDemo?.fileId ?? null,\n };\n try {\n const created = await addExercise.mutateAsync(input);\n analytics.emitFact('exercise', 'created', { workspaceId, entityId: created.id });\n setExerciseTarget(null);\n } catch (e) {\n setExerciseErr(e instanceof Error ? e.message : 'Failed to add exercise.');\n }\n };\n\n return (\n <PageLayout\n title={program.name}\n actions={\n <>\n {program.status === 'DRAFT' && (\n <Button onClick={() => void doTransition('ACTIVE')} disabled={transition.isPending}>\n <Play className=\"mr-1 h-4 w-4\" /> Activate\n </Button>\n )}\n {program.status === 'ACTIVE' && (\n <>\n <Button\n onClick={() => void doTransition('COMPLETED')}\n disabled={transition.isPending}\n >\n <CheckCircle2 className=\"mr-1 h-4 w-4\" /> Complete\n </Button>\n <Button variant=\"outline\" onClick={() => navigateTo('train')}>\n <Flame className=\"mr-1 h-4 w-4\" /> Start a session\n </Button>\n </>\n )}\n {program.status !== 'ARCHIVED' && (\n <Button variant=\"outline\" onClick={openEdit} disabled={updateProgram.isPending}>\n <Pencil className=\"mr-1 h-4 w-4\" /> Edit\n </Button>\n )}\n {program.status !== 'ARCHIVED' && (\n <Button\n variant=\"outline\"\n onClick={() => void doArchive()}\n disabled={archiveM.isPending}\n >\n <Archive className=\"mr-1 h-4 w-4\" /> Archive\n </Button>\n )}\n {canStructure && (\n <Button onClick={openAddWorkout}>\n <Plus className=\"mr-1 h-4 w-4\" /> Add workout\n </Button>\n )}\n </>\n }\n >\n <button\n type=\"button\"\n onClick={() => navigateTo('programs')}\n className=\"mb-4 inline-flex items-center gap-1 text-sm font-medium text-accent hover:underline\"\n >\n <ArrowLeft className=\"h-4 w-4\" /> All programs\n </button>\n\n {/* Header card */}\n <motion.div\n initial={reduce ? false : { opacity: 0, y: 10 }}\n animate={{ opacity: 1, y: 0 }}\n transition={reduce ? { duration: 0 } : { duration: 0.35, ease: MOTION_EASE }}\n >\n <GlassCard treatment=\"glass\" glow={isActive} className=\"p-6\">\n <div className=\"flex flex-wrap items-start justify-between gap-4\">\n <div className=\"min-w-0\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <IconChip\n icon={<GoalIcon className=\"h-5 w-5\" />}\n tint={goalMeta.tint}\n className=\"size-10\"\n />\n <h2 className=\"text-xl font-semibold text-text-primary\">{program.name}</h2>\n <StatusBadge status={program.status} />\n </div>\n <div className=\"mt-3 flex flex-wrap gap-1.5\">\n <Chip>Phase · {formatEnumLabel(program.phase)}</Chip>\n <Chip>Level · {formatEnumLabel(program.level)}</Chip>\n <Chip icon={<GoalIcon className=\"h-3.5 w-3.5\" />} tint={goalMeta.tint}>\n {formatEnumLabel(program.goalType)}\n </Chip>\n <Chip icon={<CalendarDays className=\"h-3.5 w-3.5\" />}>\n {formatNumber(program.weeks)} wk × {formatNumber(program.daysPerWeek)} days\n </Chip>\n </div>\n <p className=\"mt-3 inline-flex items-center gap-1.5 text-sm text-text-secondary\">\n <CalendarDays className=\"h-4 w-4 text-text-tertiary\" />\n {formatDate(program.startDate)} – {formatDate(program.endDate)}\n </p>\n {(program.tags ?? []).length > 0 && (\n <div className=\"mt-3 flex flex-wrap gap-1.5\">\n {(program.tags ?? []).map((tag) => (\n <span\n key={tag}\n className=\"inline-flex items-center rounded-full bg-[var(--color-accent-soft)] px-2 py-0.5 text-xs font-medium text-text-secondary\"\n >\n #{tag}\n </span>\n ))}\n </div>\n )}\n {program.description && (\n <p className=\"mt-3 max-w-2xl text-sm text-text-secondary\">{program.description}</p>\n )}\n </div>\n </div>\n\n {/* Editable platform tags (the inline #tags above are a read-only supplement). */}\n <div className=\"mt-5 border-t border-border-seam pt-4\">\n <MybTags\n entityType=\"mybodyshield-program\"\n entityId={program.id}\n label=\"Platform tags\"\n />\n </div>\n\n {actionErr && (\n <p className=\"mt-4 rounded-lg bg-status-error-bg-subtle px-3 py-2 text-sm text-status-error-text\">\n {actionErr}\n </p>\n )}\n </GlassCard>\n </motion.div>\n\n {/* Workouts */}\n <Section\n title=\"Workouts\"\n description=\"Sessions in this block, ordered by week, day & sequence.\"\n >\n {!canStructure && (\n <p className=\"mb-4 inline-flex items-center gap-1.5 rounded-lg border border-border-seam bg-bg-sunken/60 px-3 py-2 text-xs text-text-tertiary\">\n <Lock className=\"h-3.5 w-3.5 shrink-0\" />\n This program is {formatEnumLabel(program.status).toLowerCase()} — its structure is\n locked and can no longer be edited.\n </p>\n )}\n {workouts.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n icon={<Dumbbell className=\"h-10 w-10\" />}\n title=\"No workouts yet\"\n description={\n canStructure\n ? 'Add workouts to lay out the training week, then attach exercises to each one.'\n : 'This program has no workouts, and its structure is locked in its current status.'\n }\n action={\n canStructure ? (\n <Button onClick={openAddWorkout}>\n <Plus className=\"mr-1 h-4 w-4\" /> Add workout\n </Button>\n ) : undefined\n }\n />\n ) : (\n <motion.div\n className=\"grid grid-cols-1 gap-4 xl:grid-cols-2\"\n variants={reduce ? undefined : STAGGER_CONTAINER}\n initial={reduce ? false : 'hidden'}\n animate=\"show\"\n >\n {workouts.map((workout) => (\n <motion.div\n key={workout.id}\n className=\"h-full\"\n variants={reduce ? undefined : RISE_ITEM}\n >\n <WorkoutCard\n workout={workout}\n canEdit={canStructure}\n onAddExercise={openAddExercise}\n />\n </motion.div>\n ))}\n </motion.div>\n )}\n </Section>\n\n {/* Coach & member notes */}\n <MybDiscussion\n entityType=\"mybodyshield-program\"\n entityId={program.id}\n title=\"Program notes\"\n description=\"Coach and member notes for this training block.\"\n />\n\n {/* Add-workout dialog */}\n <ResponsiveDialog open={workoutOpen} onOpenChange={setWorkoutOpen}>\n <ResponsiveDialogContent className=\"sm:max-w-lg\">\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>Add workout</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n Add a training session to this program. Attach exercises once it's created.\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n\n <div className=\"space-y-4 py-2\">\n {workoutErr && (\n <p className=\"rounded-lg bg-status-error-bg-subtle px-3 py-2 text-sm text-status-error-text\">\n {workoutErr}\n </p>\n )}\n\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-wk-name\">Name</Label>\n <Input\n id=\"myb-wk-name\"\n value={workoutForm.name}\n onChange={(e) => setWorkoutForm({ ...workoutForm, name: e.target.value })}\n placeholder=\"e.g. Upper Body — Push\"\n />\n </div>\n\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-wk-type\">Type</Label>\n <EnumSelect\n id=\"myb-wk-type\"\n value={workoutForm.type}\n onChange={(v) => setWorkoutForm({ ...workoutForm, type: v })}\n options={WORKOUT_TYPES}\n />\n </div>\n\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-3\">\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-wk-week\">Week</Label>\n <Input\n id=\"myb-wk-week\"\n type=\"number\"\n min={1}\n value={workoutForm.weekNumber}\n onChange={(e) => setWorkoutForm({ ...workoutForm, weekNumber: e.target.value })}\n placeholder=\"1\"\n />\n </div>\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-wk-day\">Day</Label>\n <Input\n id=\"myb-wk-day\"\n type=\"number\"\n min={1}\n max={7}\n value={workoutForm.dayOfWeek}\n onChange={(e) => setWorkoutForm({ ...workoutForm, dayOfWeek: e.target.value })}\n placeholder=\"1\"\n />\n </div>\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-wk-dur\">Duration (min)</Label>\n <Input\n id=\"myb-wk-dur\"\n type=\"number\"\n min={0}\n value={workoutForm.durationMin}\n onChange={(e) => setWorkoutForm({ ...workoutForm, durationMin: e.target.value })}\n placeholder=\"60\"\n />\n </div>\n </div>\n </div>\n\n <ResponsiveDialogFooter>\n <Button\n variant=\"outline\"\n onClick={() => setWorkoutOpen(false)}\n disabled={addWorkout.isPending}\n >\n Cancel\n </Button>\n <Button onClick={() => void submitWorkout()} disabled={addWorkout.isPending}>\n {addWorkout.isPending ? 'Adding…' : 'Add workout'}\n </Button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n\n {/* Add-exercise dialog */}\n <ResponsiveDialog\n open={exerciseTarget !== null}\n onOpenChange={(open) => {\n if (!open) setExerciseTarget(null);\n }}\n >\n <ResponsiveDialogContent className=\"sm:max-w-lg\">\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>Add exercise</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n Add a movement with its target sets, reps, load & rest.\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n\n <div className=\"space-y-4 py-2\">\n {exerciseErr && (\n <p className=\"rounded-lg bg-status-error-bg-subtle px-3 py-2 text-sm text-status-error-text\">\n {exerciseErr}\n </p>\n )}\n\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-ex-name\">Name</Label>\n <Input\n id=\"myb-ex-name\"\n value={exerciseForm.name}\n onChange={(e) => setExerciseForm({ ...exerciseForm, name: e.target.value })}\n placeholder=\"e.g. Barbell Bench Press\"\n />\n </div>\n\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-ex-muscle\">Muscle group</Label>\n <EnumSelect\n id=\"myb-ex-muscle\"\n value={exerciseForm.muscleGroup}\n onChange={(v) => setExerciseForm({ ...exerciseForm, muscleGroup: v })}\n options={MUSCLE_GROUPS}\n />\n </div>\n\n <div className=\"grid grid-cols-2 gap-4 sm:grid-cols-4\">\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-ex-sets\">Sets</Label>\n <Input\n id=\"myb-ex-sets\"\n type=\"number\"\n min={0}\n value={exerciseForm.sets}\n onChange={(e) => setExerciseForm({ ...exerciseForm, sets: e.target.value })}\n placeholder=\"3\"\n />\n </div>\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-ex-reps\">Reps</Label>\n <Input\n id=\"myb-ex-reps\"\n type=\"number\"\n min={0}\n value={exerciseForm.reps}\n onChange={(e) => setExerciseForm({ ...exerciseForm, reps: e.target.value })}\n placeholder=\"10\"\n />\n </div>\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-ex-weight\">Weight (kg)</Label>\n <Input\n id=\"myb-ex-weight\"\n type=\"number\"\n min={0}\n step=\"0.5\"\n value={exerciseForm.targetWeight}\n onChange={(e) =>\n setExerciseForm({ ...exerciseForm, targetWeight: e.target.value })\n }\n placeholder=\"60\"\n />\n </div>\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-ex-rest\">Rest (sec)</Label>\n <Input\n id=\"myb-ex-rest\"\n type=\"number\"\n min={0}\n value={exerciseForm.restSeconds}\n onChange={(e) =>\n setExerciseForm({ ...exerciseForm, restSeconds: e.target.value })\n }\n placeholder=\"90\"\n />\n </div>\n </div>\n\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-ex-instructions\">Instructions</Label>\n <Textarea\n id=\"myb-ex-instructions\"\n rows={3}\n value={exerciseForm.instructions}\n onChange={(e) => setExerciseForm({ ...exerciseForm, instructions: e.target.value })}\n placeholder=\"Cues, tempo, form notes…\"\n />\n </div>\n\n <EvidenceUpload\n id=\"myb-ex-demo\"\n label=\"Demo media (optional)\"\n value={exerciseDemo}\n onChange={setExerciseDemo}\n accept=\"video/*,image/*\"\n disabled={addExercise.isPending}\n hint=\"Attach a short demo clip or image to show how the movement is performed.\"\n />\n </div>\n\n <ResponsiveDialogFooter>\n <Button\n variant=\"outline\"\n onClick={() => setExerciseTarget(null)}\n disabled={addExercise.isPending}\n >\n Cancel\n </Button>\n <Button onClick={() => void submitExercise()} disabled={addExercise.isPending}>\n {addExercise.isPending ? 'Adding…' : 'Add exercise'}\n </Button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n\n {/* Edit-program dialog */}\n <ResponsiveDialog open={editOpen} onOpenChange={setEditOpen}>\n <ResponsiveDialogContent className=\"sm:max-w-lg\">\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>Edit program</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n Update this program's plan, goal & schedule. Workouts stay untouched.\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n\n <div className=\"space-y-4 py-2\">\n {editErr && (\n <p className=\"rounded-lg bg-status-error-bg-subtle px-3 py-2 text-sm text-status-error-text\">\n {editErr}\n </p>\n )}\n\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-edit-name\">Name</Label>\n <Input\n id=\"myb-edit-name\"\n value={editForm.name}\n onChange={(e) => setEditForm({ ...editForm, name: e.target.value })}\n placeholder=\"e.g. Hypertrophy Block A\"\n />\n </div>\n\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-edit-desc\">Description</Label>\n <Textarea\n id=\"myb-edit-desc\"\n rows={3}\n value={editForm.description}\n onChange={(e) => setEditForm({ ...editForm, description: e.target.value })}\n placeholder=\"Focus, progression scheme, notes…\"\n />\n </div>\n\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-3\">\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-edit-goal\">Goal type</Label>\n <EnumSelect\n id=\"myb-edit-goal\"\n value={editForm.goalType}\n onChange={(v) => setEditForm({ ...editForm, goalType: v })}\n options={GOAL_TYPES}\n />\n </div>\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-edit-level\">Level</Label>\n <EnumSelect\n id=\"myb-edit-level\"\n value={editForm.level}\n onChange={(v) => setEditForm({ ...editForm, level: v })}\n options={LEVELS}\n />\n </div>\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-edit-phase\">Phase</Label>\n <EnumSelect\n id=\"myb-edit-phase\"\n value={editForm.phase}\n onChange={(v) => setEditForm({ ...editForm, phase: v })}\n options={PHASES}\n />\n </div>\n </div>\n\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2\">\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-edit-weeks\">Weeks</Label>\n <Input\n id=\"myb-edit-weeks\"\n type=\"number\"\n min={1}\n value={editForm.weeks}\n onChange={(e) => setEditForm({ ...editForm, weeks: e.target.value })}\n placeholder=\"8\"\n />\n </div>\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-edit-days\">Days / week</Label>\n <Input\n id=\"myb-edit-days\"\n type=\"number\"\n min={1}\n max={7}\n value={editForm.daysPerWeek}\n onChange={(e) => setEditForm({ ...editForm, daysPerWeek: e.target.value })}\n placeholder=\"4\"\n />\n </div>\n </div>\n\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2\">\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-edit-start\">Start date</Label>\n <Input\n id=\"myb-edit-start\"\n type=\"date\"\n value={editForm.startDate}\n onChange={(e) => setEditForm({ ...editForm, startDate: e.target.value })}\n />\n </div>\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-edit-end\">End date</Label>\n <Input\n id=\"myb-edit-end\"\n type=\"date\"\n value={editForm.endDate}\n onChange={(e) => setEditForm({ ...editForm, endDate: e.target.value })}\n />\n </div>\n </div>\n </div>\n\n <ResponsiveDialogFooter>\n <Button\n variant=\"outline\"\n onClick={() => setEditOpen(false)}\n disabled={updateProgram.isPending}\n >\n Cancel\n </Button>\n <Button onClick={() => void submitEdit()} disabled={updateProgram.isPending}>\n {updateProgram.isPending ? 'Saving…' : 'Save changes'}\n </Button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
@@ -18,7 +18,7 @@ import { listPendingActionsForSession as he } from "../../lib/offlineSessionQueu
|
|
|
18
18
|
import { useOfflineSessionSync as ge } from "../../lib/useOfflineSessionSync.js";
|
|
19
19
|
import { useCallback as _e, useEffect as ve, useMemo as l, useState as u } from "react";
|
|
20
20
|
import { AlertTriangle as ye, ArrowLeft as be, CheckCircle2 as xe, ChevronLeft as Se, ChevronRight as Ce, Dumbbell as d, Flame as we, Gauge as Te, ListChecks as Ee, Plus as f, Ruler as De, Timer as p, Trophy as Oe, Weight as ke, WifiOff as Ae, X as je } from "lucide-react";
|
|
21
|
-
import { useParams as Me } from "react-router
|
|
21
|
+
import { useParams as Me } from "react-router";
|
|
22
22
|
import { Fragment as Ne, jsx as m, jsxs as h } from "react/jsx-runtime";
|
|
23
23
|
import { Button as g, GlassCard as _, Input as v, Label as y, ResponsiveDialog as Pe, ResponsiveDialogContent as Fe, ResponsiveDialogDescription as Ie, ResponsiveDialogFooter as Le, ResponsiveDialogHeader as Re, ResponsiveDialogTitle as ze, Switch as Be, Textarea as Ve } from "@burdenoff/fe-libs/ui";
|
|
24
24
|
//#region src/mybodyshield/pages/train/SessionDetailPage.tsx
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SessionDetailPage.js","names":[],"sources":["../../../../src/mybodyshield/pages/train/SessionDetailPage.tsx"],"sourcesContent":["/**\n * Train — workout session detail & live logging.\n * @module mybodyshield/pages/train\n *\n * Renders a single workout session (real `mybSession`, including its `sets`).\n * While a session is IN_PROGRESS you can log sets and complete it; completed /\n * scheduled sessions render read-only. Sets are grouped by exercise. No mock\n * data — everything comes from the myb* hooks.\n */\n\nimport { useCallback, useEffect, useMemo, useState } from 'react';\nimport { motion, useReducedMotion } from 'framer-motion';\nimport { useParams } from 'react-router-dom';\nimport {\n ArrowLeft,\n Plus,\n CheckCircle2,\n Dumbbell,\n Weight,\n Timer,\n Flame,\n Ruler,\n Gauge,\n AlertTriangle,\n ChevronLeft,\n ChevronRight,\n ListChecks,\n Trophy,\n WifiOff,\n X,\n} from 'lucide-react';\nimport {\n Button,\n GlassCard,\n Label,\n Input,\n Textarea,\n Switch,\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n ResponsiveDialogDescription,\n ResponsiveDialogFooter,\n} from '@burdenoff/fe-libs/ui';\nimport {\n PageLayout,\n MetricsCard,\n StatusBadge,\n EmptyState,\n LoadingView,\n ErrorView,\n MybDiscussion,\n} from '../../components';\nimport { IconChip } from '../_shared/visuals';\nimport { useMyBodyShield } from '../../context';\nimport {\n useMybSession,\n useMybLogSet,\n useMybCompleteSession,\n useMybWorkoutExercises,\n useMybExerciseBestSet,\n useMyBodyShieldAnalytics,\n} from '../../hooks';\nimport {\n listPendingActionsForSession,\n type PendingSessionAction,\n} from '../../lib/offlineSessionQueue';\nimport { useOfflineSessionSync } from '../../lib/useOfflineSessionSync';\nimport {\n formatDateTime,\n formatRelativeTime,\n formatNumber,\n formatWeight,\n formatKcal,\n formatDuration,\n formatEnumLabel,\n} from '../../utils/formatters';\nimport type { MybSessionSet, MybExercise } from '../../types';\n\n/**\n * Heuristic for \"this failure means we're offline, not that the request was\n * rejected\" (BOFF-4219). `graphqlFetch`'s underlying `fetch()` throws a\n * `TypeError` when the network is unreachable (no HTTP response at all);\n * a real GraphQL/backend error instead resolves with an `errors` array that\n * `mybQuery` turns into a plain `Error` with a server-authored message, so\n * checking `navigator.onLine` first and falling back to the error shape\n * keeps a genuine validation error (e.g. a deleted session) from being\n * mistaken for a connectivity gap and silently queued.\n */\nfunction isLikelyOffline(err: unknown): boolean {\n if (typeof navigator !== 'undefined' && !navigator.onLine) return true;\n if (err instanceof TypeError) return true;\n const msg = err instanceof Error ? err.message.toLowerCase() : '';\n return msg.includes('failed to fetch') || msg.includes('load failed') || msg.includes('network');\n}\n\n/** Render a queued (not-yet-synced) set the same shape as a real MybSessionSet for display. */\nfunction pendingActionToDisplaySet(action: PendingSessionAction, sessionId: string): MybSessionSet {\n const input = action.input as {\n exerciseName: string;\n setNumber?: number | null;\n reps?: number | null;\n weight?: number | null;\n distanceM?: number | null;\n durationSec?: number | null;\n isPersonalRecord?: boolean | null;\n };\n return {\n id: `pending-${action.clientActionId}`,\n workspaceId: '',\n sessionId,\n exerciseName: input.exerciseName,\n setNumber: input.setNumber ?? 1,\n reps: input.reps ?? 0,\n weight: input.weight ?? 0,\n distanceM: input.distanceM ?? 0,\n durationSec: input.durationSec ?? 0,\n isPersonalRecord: Boolean(input.isPersonalRecord),\n createdAt: action.capturedAt,\n updatedAt: action.capturedAt,\n };\n}\n\n/** Rest timer default when a set isn't part of a guided plan exercise. */\nconst DEFAULT_REST_SECONDS = 90;\n\n/** Stable empty-array reference so the \"no plan\" case never changes identity across renders. */\nconst NO_EXERCISES: readonly MybExercise[] = [];\n\n/**\n * Does a newly-logged `weight`/`reps` beat the member's prior best for this\n * exercise? Mirrors the backend's own ranking (weight desc, then reps desc).\n * `null`/`undefined` best (never logged before) is NOT treated as a record —\n * there is nothing yet to break — matching `mybExerciseBestSet`'s contract.\n */\nfunction beatsBest(weight: number, reps: number, best: MybSessionSet | null | undefined): boolean {\n if (!best) return false;\n if (weight > best.weight) return true;\n if (weight === best.weight && reps > best.reps) return true;\n return false;\n}\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/** Stagger container + rise-in child variants for grouped card entrances. */\nconst STAGGER_CONTAINER = {\n hidden: {},\n show: { transition: { staggerChildren: 0.06 } },\n};\nconst RISE_ITEM = {\n hidden: { opacity: 0, y: 12 },\n show: { opacity: 1, y: 0, transition: { duration: 0.35, ease: MOTION_EASE } },\n};\n\n/** Parse an optional numeric string into a number, or undefined when blank. */\nfunction numOrUndef(value: string): number | undefined {\n const trimmed = value.trim();\n if (!trimmed) return undefined;\n const n = Number(trimmed);\n return Number.isFinite(n) ? n : undefined;\n}\n\ninterface LogSetForm {\n exerciseName: string;\n setNumber: string;\n reps: string;\n weight: string;\n distanceM: string;\n durationSec: string;\n isPersonalRecord: boolean;\n}\n\nconst EMPTY_LOG_FORM: LogSetForm = {\n exerciseName: '',\n setNumber: '',\n reps: '',\n weight: '',\n distanceM: '',\n durationSec: '',\n isPersonalRecord: false,\n};\n\ninterface CompleteForm {\n caloriesBurned: string;\n perceivedExertion: string;\n notes: string;\n}\n\nconst EMPTY_COMPLETE_FORM: CompleteForm = { caloriesBurned: '', perceivedExertion: '', notes: '' };\n\nexport function SessionDetailPage() {\n const { navigateTo, workspaceId } = useMyBodyShield();\n const reduce = useReducedMotion();\n const { id } = useParams<{ id: string }>();\n const analytics = useMyBodyShieldAnalytics();\n\n const session = useMybSession(id);\n const logSet = useMybLogSet();\n const completeSession = useMybCompleteSession();\n const exercisesQuery = useMybWorkoutExercises(session.data?.workoutId ?? undefined);\n\n // BOFF-4219: offline-first queue for logging sets / completing a session\n // that was started while online but hits a connectivity gap mid-workout.\n const offlineSync = useOfflineSessionSync(\n (input) => logSet.mutateAsync(input),\n (vars) => completeSession.mutateAsync(vars),\n () => session.refetch()\n );\n const [pendingActions, setPendingActions] = useState<PendingSessionAction[]>([]);\n const refreshPendingForSession = useCallback(() => {\n if (!id) return;\n void listPendingActionsForSession(id).then(setPendingActions);\n }, [id]);\n useEffect(() => {\n refreshPendingForSession();\n }, [refreshPendingForSession, offlineSync.pendingCount]);\n const pendingSets = useMemo(\n () =>\n id\n ? pendingActions\n .filter((a) => a.type === 'LOG_SET')\n .map((a) => pendingActionToDisplaySet(a, id))\n : [],\n [pendingActions, id]\n );\n const pendingComplete = pendingActions.some((a) => a.type === 'COMPLETE_SESSION');\n\n const [logOpen, setLogOpen] = useState(false);\n const [logForm, setLogForm] = useState<LogSetForm>(EMPTY_LOG_FORM);\n const [logError, setLogError] = useState<string | null>(null);\n const [prTouched, setPrTouched] = useState(false);\n\n const [completeOpen, setCompleteOpen] = useState(false);\n const [completeForm, setCompleteForm] = useState<CompleteForm>(EMPTY_COMPLETE_FORM);\n const [completeError, setCompleteError] = useState<string | null>(null);\n\n // Guided next-exercise stepper (BOFF-4096): `null` means \"auto-follow\" —\n // the active step tracks the first not-yet-fully-logged planned exercise;\n // an explicit index means the member manually paged away from that.\n const [manualExerciseIdx, setManualExerciseIdx] = useState<number | null>(null);\n const [prevWorkoutId, setPrevWorkoutId] = useState<string | null | undefined>(\n session.data?.workoutId\n );\n\n // Rest timer (BOFF-4096): a wall-clock deadline rather than a plain\n // countdown-in-state, so the remaining time stays correct even if the tab\n // was backgrounded and the 1s interval was throttled/paused.\n const [restEndsAt, setRestEndsAt] = useState<number | null>(null);\n const [restRemaining, setRestRemaining] = useState(0);\n\n const bestSet = useMybExerciseBestSet(logForm.exerciseName);\n\n const data = session.data;\n // BOFF-4219: sets queued offline aren't in the server-fetched `data.sets`\n // yet — merge them in so a member sees what they logged immediately,\n // even before the queue has had a chance to sync.\n const sets = useMemo<MybSessionSet[]>(\n () => [...(data?.sets ?? []), ...pendingSets],\n [data?.sets, pendingSets]\n );\n\n const groups = useMemo(() => {\n const map = new Map<string, MybSessionSet[]>();\n for (const st of sets) {\n const arr = map.get(st.exerciseName) ?? [];\n arr.push(st);\n map.set(st.exerciseName, arr);\n }\n return Array.from(map.entries()).map(([name, list]) => ({\n name,\n sets: list.slice().sort((a, b) => a.setNumber - b.setNumber),\n }));\n }, [sets]);\n\n const totalVolume = useMemo(() => sets.reduce((acc, st) => acc + st.reps * st.weight, 0), [sets]);\n\n const startedAt = data?.startedAt;\n const completedAt = data?.completedAt;\n const durationSec = useMemo(() => {\n if (!startedAt || !completedAt) return undefined;\n const start = new Date(startedAt).getTime();\n const end = new Date(completedAt).getTime();\n if (!Number.isFinite(start) || !Number.isFinite(end) || end < start) return undefined;\n return (end - start) / 1000;\n }, [startedAt, completedAt]);\n\n const exercises = exercisesQuery.data ?? NO_EXERCISES;\n\n const loggedCountByExercise = useMemo(() => {\n const map = new Map<string, number>();\n for (const g of groups) map.set(g.name, g.sets.length);\n return map;\n }, [groups]);\n\n const autoExerciseIdx = useMemo(() => {\n if (exercises.length === 0) return 0;\n const idx = exercises.findIndex(\n (ex) => (loggedCountByExercise.get(ex.name) ?? 0) < Math.max(ex.sets, 1)\n );\n return idx === -1 ? exercises.length - 1 : idx;\n }, [exercises, loggedCountByExercise]);\n\n const activeExerciseIdx = Math.min(\n manualExerciseIdx ?? autoExerciseIdx,\n Math.max(exercises.length - 1, 0)\n );\n const activeExercise: MybExercise | undefined = exercises[activeExerciseIdx];\n const allExercisesDone =\n exercises.length > 0 &&\n exercises.every((ex) => (loggedCountByExercise.get(ex.name) ?? 0) >= Math.max(ex.sets, 1));\n\n // A different session's plan invalidates any manual stepper override. This\n // adjusts state during render (React's documented alternative to an\n // effect for \"reset state when a prop changes\") rather than an effect, so\n // it never causes the extra reset-then-settle render an effect would.\n if (session.data?.workoutId !== prevWorkoutId) {\n setPrevWorkoutId(session.data?.workoutId);\n setManualExerciseIdx(null);\n }\n\n // Tick the rest-timer countdown against the wall-clock deadline. The\n // effect only *subscribes* to the interval — every state update happens\n // inside the (deferred) tick callback, never synchronously in the effect\n // body itself, so starting/stopping the timer never forces an extra render.\n useEffect(() => {\n if (restEndsAt == null) return;\n const interval = window.setInterval(() => {\n const remaining = Math.max(0, Math.ceil((restEndsAt - Date.now()) / 1000));\n setRestRemaining(remaining);\n if (remaining <= 0) setRestEndsAt(null);\n }, 1000);\n return () => window.clearInterval(interval);\n }, [restEndsAt]);\n\n // Live auto-PR detection (BOFF-4096): derived at render time, not synced\n // via an effect — whether the in-progress reps/weight would beat the\n // member's historical best, unless they've manually overridden the switch\n // for this exercise.\n const weightNum = numOrUndef(logForm.weight) ?? 0;\n const repsNum = numOrUndef(logForm.reps) ?? 0;\n const autoPredictedPR =\n logForm.exerciseName.trim().length > 0 && beatsBest(weightNum, repsNum, bestSet.data);\n const effectiveIsPersonalRecord = prTouched ? logForm.isPersonalRecord : autoPredictedPR;\n\n if (!id) {\n return (\n <PageLayout title=\"Workout session\">\n <ErrorView message=\"No session was specified.\" onRetry={() => navigateTo('train')} />\n </PageLayout>\n );\n }\n\n if (session.isLoading) {\n return (\n <PageLayout title=\"Workout session\">\n <LoadingView label=\"Loading session…\" />\n </PageLayout>\n );\n }\n\n if (session.isError || !data) {\n return (\n <PageLayout title=\"Workout session\">\n <ErrorView\n title=\"Session not found\"\n message=\"This workout session may have been removed, or you don't have access to it.\"\n onRetry={() => session.refetch()}\n />\n </PageLayout>\n );\n }\n\n const isLive = data.status === 'IN_PROGRESS';\n\n const summary = data.completedAt\n ? `Completed ${formatRelativeTime(data.completedAt)}`\n : data.startedAt\n ? `Started ${formatRelativeTime(data.startedAt)}`\n : data.scheduledFor\n ? `Scheduled ${formatRelativeTime(data.scheduledFor)}`\n : `Created ${formatRelativeTime(data.createdAt)}`;\n\n const openLog = () => {\n setLogForm(EMPTY_LOG_FORM);\n setLogError(null);\n setPrTouched(false);\n setLogOpen(true);\n };\n\n /** Guided-stepper quick action: pre-fill the log dialog from a planned exercise. */\n const openLogForExercise = (exercise: MybExercise) => {\n const alreadyLogged = loggedCountByExercise.get(exercise.name) ?? 0;\n setLogForm({\n ...EMPTY_LOG_FORM,\n exerciseName: exercise.name,\n setNumber: String(alreadyLogged + 1),\n reps: exercise.reps > 0 ? String(exercise.reps) : '',\n weight: exercise.targetWeight > 0 ? String(exercise.targetWeight) : '',\n });\n setLogError(null);\n setPrTouched(false);\n setLogOpen(true);\n };\n\n const submitLog = async () => {\n setLogError(null);\n const exerciseName = logForm.exerciseName.trim();\n if (!exerciseName) {\n setLogError('Exercise name is required.');\n return;\n }\n const input = {\n exerciseName,\n setNumber: numOrUndef(logForm.setNumber),\n reps: numOrUndef(logForm.reps),\n weight: numOrUndef(logForm.weight),\n distanceM: numOrUndef(logForm.distanceM),\n durationSec: numOrUndef(logForm.durationSec),\n isPersonalRecord: effectiveIsPersonalRecord,\n };\n const advanceForm = (nextSetNumber: number) => {\n setLogForm({ ...EMPTY_LOG_FORM, exerciseName, setNumber: String(nextSetNumber) });\n setPrTouched(false);\n const restSeconds =\n activeExercise && activeExercise.name === exerciseName && activeExercise.restSeconds > 0\n ? activeExercise.restSeconds\n : DEFAULT_REST_SECONDS;\n setRestEndsAt(Date.now() + restSeconds * 1000);\n setRestRemaining(restSeconds);\n };\n // BOFF-4219: log offline-first — don't even attempt the network round-trip\n // when we already know we're offline, so the set is captured instantly.\n if (!offlineSync.isOnline) {\n await offlineSync.enqueueLogSet(id, input);\n refreshPendingForSession();\n advanceForm((numOrUndef(logForm.setNumber) ?? sets.length) + 1);\n return;\n }\n try {\n const created = await logSet.mutateAsync({ sessionId: id, ...input });\n advanceForm(created.setNumber + 1);\n } catch (e) {\n if (isLikelyOffline(e)) {\n await offlineSync.enqueueLogSet(id, input);\n refreshPendingForSession();\n advanceForm((numOrUndef(logForm.setNumber) ?? sets.length) + 1);\n return;\n }\n setLogError(e instanceof Error ? e.message : 'Failed to log the set.');\n }\n };\n\n const openComplete = () => {\n setCompleteForm(EMPTY_COMPLETE_FORM);\n setCompleteError(null);\n setCompleteOpen(true);\n };\n\n const submitComplete = async () => {\n setCompleteError(null);\n const input = {\n caloriesBurned: numOrUndef(completeForm.caloriesBurned),\n perceivedExertion: numOrUndef(completeForm.perceivedExertion),\n notes: completeForm.notes.trim() ? completeForm.notes.trim() : undefined,\n };\n if (!offlineSync.isOnline) {\n await offlineSync.enqueueCompleteSession(id, input);\n refreshPendingForSession();\n setCompleteOpen(false);\n return;\n }\n try {\n await completeSession.mutateAsync({ id, input });\n setCompleteOpen(false);\n } catch (e) {\n if (isLikelyOffline(e)) {\n await offlineSync.enqueueCompleteSession(id, input);\n refreshPendingForSession();\n setCompleteOpen(false);\n return;\n }\n setCompleteError(e instanceof Error ? e.message : 'Failed to complete the session.');\n }\n };\n\n const actions = isLive ? (\n <>\n <Button variant=\"outline\" onClick={openLog}>\n <Plus className=\"h-4 w-4\" /> Log set\n </Button>\n <Button onClick={openComplete} disabled={pendingComplete}>\n <CheckCircle2 className=\"h-4 w-4\" /> {pendingComplete ? 'Pending sync…' : 'Complete'}\n </Button>\n </>\n ) : undefined;\n\n return (\n <PageLayout title=\"Workout session\" description={summary} actions={actions}>\n <button\n type=\"button\"\n onClick={() => navigateTo('train')}\n className=\"mb-4 inline-flex items-center gap-1.5 text-sm font-medium text-text-secondary hover:text-text-primary\"\n >\n <ArrowLeft className=\"h-4 w-4\" /> Back to sessions\n </button>\n\n {/* BOFF-4219: offline queue status — only shown when it's relevant, so\n nothing changes for a member who never leaves connectivity. */}\n {(!offlineSync.isOnline || pendingActions.length > 0) && (\n <div className=\"mb-4 flex items-center gap-2 rounded-xl border border-status-warning-text/40 bg-status-warning-bg-subtle px-4 py-2.5 text-sm text-text-primary\">\n <WifiOff className=\"h-4 w-4 shrink-0 text-status-warning-text\" />\n <span>\n {!offlineSync.isOnline\n ? `You're offline — logged sets and completion will sync automatically once you're back online${\n pendingActions.length > 0 ? ` (${pendingActions.length} queued)` : ''\n }.`\n : `Syncing ${pendingActions.length} queued ${pendingActions.length === 1 ? 'action' : 'actions'}…`}\n </span>\n {offlineSync.isOnline && pendingActions.length > 0 && (\n <Button\n variant=\"ghost\"\n onClick={offlineSync.syncNow}\n className=\"ml-auto h-7 px-2 text-xs\"\n >\n Retry now\n </Button>\n )}\n </div>\n )}\n\n {/* Header card */}\n <motion.div\n initial={reduce ? false : { opacity: 0, y: 10 }}\n animate={{ opacity: 1, y: 0 }}\n transition={reduce ? { duration: 0 } : { duration: 0.35, ease: MOTION_EASE }}\n >\n <GlassCard treatment=\"glass\" glow={isLive} className=\"p-6\">\n <div className=\"flex flex-wrap items-start justify-between gap-3\">\n <div className=\"flex items-center gap-3\">\n <IconChip\n icon={isLive ? <Flame className=\"h-5 w-5\" /> : <Dumbbell className=\"h-5 w-5\" />}\n tint={isLive ? 'var(--color-accent-teal)' : 'var(--color-accent-primary)'}\n />\n <div>\n <div className=\"flex items-center gap-2\">\n <StatusBadge status={data.status} />\n </div>\n <p className=\"mt-1 text-sm text-text-secondary\">{summary}</p>\n </div>\n </div>\n </div>\n\n <div className=\"mt-5 grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n {data.scheduledFor && (\n <div>\n <p className=\"text-xs text-text-tertiary\">Scheduled for</p>\n <p className=\"mt-0.5 text-sm font-medium text-text-primary\">\n {formatDateTime(data.scheduledFor)}\n </p>\n </div>\n )}\n {data.startedAt && (\n <div>\n <p className=\"text-xs text-text-tertiary\">Started</p>\n <p className=\"mt-0.5 text-sm font-medium text-text-primary\">\n {formatDateTime(data.startedAt)}\n </p>\n </div>\n )}\n {data.completedAt && (\n <div>\n <p className=\"text-xs text-text-tertiary\">Completed</p>\n <p className=\"mt-0.5 text-sm font-medium text-text-primary\">\n {formatDateTime(data.completedAt)}\n </p>\n </div>\n )}\n {data.perceivedExertion != null && (\n <div>\n <p className=\"text-xs text-text-tertiary\">Perceived exertion</p>\n <p className=\"mt-0.5 text-sm font-medium tabular-nums text-text-primary\">\n {data.perceivedExertion}/10 RPE\n </p>\n </div>\n )}\n {data.caloriesBurned > 0 && (\n <div>\n <p className=\"text-xs text-text-tertiary\">Calories burned</p>\n <p className=\"mt-0.5 text-sm font-medium tabular-nums text-text-primary\">\n {formatKcal(data.caloriesBurned)}\n </p>\n </div>\n )}\n </div>\n\n {data.notes && (\n <div className=\"mt-5 rounded-xl bg-bg-sunken/60 p-4\">\n <p className=\"text-xs text-text-tertiary\">Notes</p>\n <p className=\"mt-1 whitespace-pre-wrap text-sm text-text-secondary\">{data.notes}</p>\n </div>\n )}\n </GlassCard>\n </motion.div>\n\n {/* Rest timer — appears after a set is logged, counts down to the next one */}\n {isLive && restRemaining > 0 && (\n <motion.div\n initial={reduce ? false : { opacity: 0, y: -6 }}\n animate={{ opacity: 1, y: 0 }}\n transition={reduce ? { duration: 0 } : { duration: 0.25, ease: MOTION_EASE }}\n className=\"mt-4\"\n >\n <GlassCard\n treatment=\"plain\"\n className=\"flex flex-wrap items-center justify-between gap-3 border border-accent/30 bg-accent/5 p-4\"\n >\n <div className=\"flex items-center gap-2.5\">\n <IconChip icon={<Timer className=\"h-4 w-4\" />} tint=\"var(--color-accent-teal)\" />\n <div>\n <p className=\"text-sm font-semibold text-text-primary\">Resting</p>\n <p className=\"text-xs tabular-nums text-text-secondary\">\n {formatDuration(restRemaining)} remaining\n </p>\n </div>\n </div>\n <div className=\"flex items-center gap-2\">\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={() => {\n setRestEndsAt((prev) => (prev ? prev + 15_000 : prev));\n setRestRemaining((r) => r + 15);\n }}\n >\n +15s\n </Button>\n <Button\n variant=\"ghost\"\n size=\"sm\"\n onClick={() => {\n setRestEndsAt(null);\n setRestRemaining(0);\n }}\n >\n <X className=\"h-4 w-4\" /> Skip\n </Button>\n </div>\n </GlassCard>\n </motion.div>\n )}\n\n {/* Guided next-exercise stepper — only for sessions tied to a planned workout */}\n {isLive && exercises.length > 0 && (\n <motion.div\n initial={reduce ? false : { opacity: 0, y: 10 }}\n animate={{ opacity: 1, y: 0 }}\n transition={reduce ? { duration: 0 } : { duration: 0.35, ease: MOTION_EASE, delay: 0.05 }}\n className=\"mt-4\"\n >\n <GlassCard treatment=\"plain\" className=\"p-4\">\n <div className=\"mb-3 flex items-center justify-between gap-2\">\n <div className=\"flex items-center gap-2.5\">\n <IconChip icon={<ListChecks className=\"h-4 w-4\" />} />\n <div>\n <h3 className=\"text-sm font-semibold text-text-primary\">Guided workout</h3>\n <p className=\"text-xs text-text-secondary\">\n {allExercisesDone\n ? 'All planned exercises logged — nice work.'\n : `Exercise ${activeExerciseIdx + 1} of ${exercises.length}`}\n </p>\n </div>\n </div>\n <div className=\"flex items-center gap-1.5\">\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={() => setManualExerciseIdx(Math.max(activeExerciseIdx - 1, 0))}\n disabled={activeExerciseIdx === 0}\n aria-label=\"Previous exercise\"\n >\n <ChevronLeft className=\"h-4 w-4\" />\n </Button>\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={() =>\n setManualExerciseIdx(Math.min(activeExerciseIdx + 1, exercises.length - 1))\n }\n disabled={activeExerciseIdx >= exercises.length - 1}\n aria-label=\"Next exercise\"\n >\n <ChevronRight className=\"h-4 w-4\" />\n </Button>\n </div>\n </div>\n\n {activeExercise && (\n <div className=\"flex flex-wrap items-center justify-between gap-3 rounded-xl bg-bg-sunken/60 p-3\">\n <div>\n <p className=\"text-sm font-semibold text-text-primary\">{activeExercise.name}</p>\n <p className=\"mt-0.5 text-xs text-text-secondary\">\n {formatEnumLabel(activeExercise.muscleGroup)} · {activeExercise.sets} ×{' '}\n {activeExercise.reps} reps\n {activeExercise.targetWeight > 0\n ? ` @ ${formatWeight(activeExercise.targetWeight)}`\n : ''}\n {activeExercise.restSeconds > 0\n ? ` · ${formatDuration(activeExercise.restSeconds)} rest`\n : ''}\n </p>\n <p className=\"mt-0.5 text-xs tabular-nums text-text-tertiary\">\n {loggedCountByExercise.get(activeExercise.name) ?? 0} of{' '}\n {Math.max(activeExercise.sets, 1)} sets logged\n </p>\n </div>\n <Button size=\"sm\" onClick={() => openLogForExercise(activeExercise)}>\n <Plus className=\"h-4 w-4\" /> Log this exercise\n </Button>\n </div>\n )}\n </GlassCard>\n </motion.div>\n )}\n\n {/* Stat cards */}\n <motion.div\n className=\"mt-6 grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4\"\n variants={reduce ? undefined : STAGGER_CONTAINER}\n initial={reduce ? false : 'hidden'}\n animate=\"show\"\n >\n <motion.div variants={reduce ? undefined : RISE_ITEM}>\n <MetricsCard\n icon={\n <span className=\"text-action-primary-bg\">\n <Dumbbell className=\"h-5 w-5\" />\n </span>\n }\n value={formatNumber(sets.length)}\n label=\"Total sets\"\n />\n </motion.div>\n <motion.div variants={reduce ? undefined : RISE_ITEM}>\n <MetricsCard\n icon={\n <span className=\"text-action-primary-bg\">\n <Weight className=\"h-5 w-5\" />\n </span>\n }\n value={`${formatNumber(totalVolume)} kg`}\n label=\"Total volume\"\n />\n </motion.div>\n <motion.div variants={reduce ? undefined : RISE_ITEM}>\n <MetricsCard\n icon={\n <span className=\"text-action-primary-bg\">\n <Timer className=\"h-5 w-5\" />\n </span>\n }\n value={durationSec != null ? formatDuration(durationSec) : '—'}\n label=\"Duration\"\n />\n </motion.div>\n <motion.div variants={reduce ? undefined : RISE_ITEM}>\n <MetricsCard\n icon={\n <span className=\"text-action-primary-bg\">\n <Flame className=\"h-5 w-5\" />\n </span>\n }\n value={data.caloriesBurned > 0 ? formatKcal(data.caloriesBurned) : '—'}\n label=\"Calories burned\"\n />\n </motion.div>\n </motion.div>\n\n {/* Logged sets */}\n <div className=\"mt-8\">\n <div className=\"mb-3 flex items-end justify-between gap-3\">\n <div>\n <h2 className=\"text-base font-semibold text-text-primary\">Logged sets</h2>\n <p className=\"mt-0.5 text-sm text-text-secondary\">\n Every set recorded during this session, grouped by exercise.\n </p>\n </div>\n {isLive && (\n <Button variant=\"outline\" size=\"sm\" onClick={openLog} className=\"shrink-0\">\n <Plus className=\"h-4 w-4\" /> Log set\n </Button>\n )}\n </div>\n\n {groups.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n icon={<Dumbbell className=\"h-10 w-10\" />}\n title=\"No sets logged yet\"\n description={\n isLive\n ? 'Log your first set to start tracking this workout.'\n : 'No sets were recorded for this session.'\n }\n action={\n isLive ? (\n <Button onClick={openLog}>\n <Plus className=\"h-4 w-4\" /> Log set\n </Button>\n ) : undefined\n }\n />\n ) : (\n <motion.div\n className=\"space-y-4\"\n variants={reduce ? undefined : STAGGER_CONTAINER}\n initial={reduce ? false : 'hidden'}\n animate=\"show\"\n >\n {groups.map((group) => {\n const prCount = group.sets.filter((st) => st.isPersonalRecord).length;\n return (\n <motion.div key={group.name} variants={reduce ? undefined : RISE_ITEM}>\n <GlassCard treatment=\"plain\" className=\"p-4 transition-shadow hover:shadow-float\">\n <div className=\"mb-3 flex items-center justify-between gap-2\">\n <div className=\"flex min-w-0 items-center gap-2.5\">\n <IconChip icon={<Dumbbell className=\"h-4 w-4\" />} />\n <h3 className=\"truncate text-sm font-semibold text-text-primary\">\n {group.name}\n </h3>\n {prCount > 0 && (\n <span className=\"inline-flex items-center rounded-full bg-status-success-bg-subtle px-2 py-0.5 text-[11px] font-semibold text-status-success-text shadow-[0_0_0_3px_color-mix(in_oklab,var(--color-status-success-text)_14%,transparent)]\">\n {prCount} PR{prCount === 1 ? '' : 's'}\n </span>\n )}\n </div>\n <span className=\"shrink-0 text-xs tabular-nums text-text-tertiary\">\n {formatNumber(group.sets.length)} {group.sets.length === 1 ? 'set' : 'sets'}\n </span>\n </div>\n <ul className=\"divide-y divide-border-seam\">\n {group.sets.map((st) => {\n const isCardio = st.durationSec > 0 || st.distanceM > 0;\n return (\n <li\n key={st.id}\n className={\n 'flex flex-wrap items-center justify-between gap-2 rounded-lg px-2 py-2 transition-colors' +\n (st.isPersonalRecord\n ? ' bg-[color-mix(in_oklab,var(--color-status-success-text)_9%,transparent)]'\n : ' hover:bg-bg-sunken/40')\n }\n >\n <div className=\"flex items-center gap-2\">\n <span className=\"text-sm font-medium text-text-primary tabular-nums\">\n Set {st.setNumber}: {formatNumber(st.reps)} ×{' '}\n {formatWeight(st.weight)}\n </span>\n {st.isPersonalRecord && (\n <span className=\"inline-flex rounded-full shadow-[0_0_0_3px_color-mix(in_oklab,var(--color-status-success-text)_16%,transparent)]\">\n <StatusBadge status=\"PR\" />\n </span>\n )}\n </div>\n <div className=\"flex flex-wrap items-center gap-x-3 gap-y-1 text-xs text-text-secondary\">\n {isCardio && st.durationSec > 0 && (\n <span className=\"inline-flex items-center gap-1 tabular-nums\">\n <Timer className=\"h-3.5 w-3.5 text-accent\" />\n {formatDuration(st.durationSec)}\n </span>\n )}\n {isCardio && st.distanceM > 0 && (\n <span className=\"inline-flex items-center gap-1 tabular-nums\">\n <Ruler className=\"h-3.5 w-3.5 text-accent\" />\n {formatNumber(st.distanceM)} m\n </span>\n )}\n </div>\n </li>\n );\n })}\n </ul>\n </GlassCard>\n </motion.div>\n );\n })}\n </motion.div>\n )}\n </div>\n\n {/* Coach feedback / discussion — available for any session */}\n <div className=\"mt-8\">\n <MybDiscussion\n entityType=\"mybodyshield-session\"\n entityId={data.id}\n title=\"Coach feedback\"\n description=\"Discuss form, cues and this session's results with your coach and team.\"\n />\n </div>\n\n {/* Log set dialog */}\n <ResponsiveDialog open={logOpen} onOpenChange={setLogOpen}>\n <ResponsiveDialogContent>\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>Log a set</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n Record reps and load for strength work, or duration and distance for cardio. The\n dialog stays open so you can log sets back-to-back.\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n\n <div className=\"space-y-4 px-1\">\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-set-exercise\">Exercise</Label>\n <Input\n id=\"myb-set-exercise\"\n value={logForm.exerciseName}\n onChange={(e) => {\n const exerciseName = e.target.value;\n setLogForm((f) => ({ ...f, exerciseName }));\n // A different exercise has a different history — let auto-PR\n // detection recompute fresh instead of keeping a stale override.\n setPrTouched(false);\n }}\n placeholder=\"e.g. Back Squat\"\n />\n </div>\n\n <div className=\"grid grid-cols-2 gap-3\">\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-set-number\">Set number</Label>\n <Input\n id=\"myb-set-number\"\n type=\"number\"\n inputMode=\"numeric\"\n min={1}\n value={logForm.setNumber}\n onChange={(e) => setLogForm((f) => ({ ...f, setNumber: e.target.value }))}\n placeholder=\"1\"\n />\n </div>\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-set-reps\">Reps</Label>\n <Input\n id=\"myb-set-reps\"\n type=\"number\"\n inputMode=\"numeric\"\n min={0}\n value={logForm.reps}\n onChange={(e) => setLogForm((f) => ({ ...f, reps: e.target.value }))}\n placeholder=\"8\"\n />\n </div>\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-set-weight\">Weight (kg)</Label>\n <Input\n id=\"myb-set-weight\"\n type=\"number\"\n inputMode=\"decimal\"\n min={0}\n step=\"0.5\"\n value={logForm.weight}\n onChange={(e) => setLogForm((f) => ({ ...f, weight: e.target.value }))}\n placeholder=\"60\"\n />\n </div>\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-set-distance\">Distance (m)</Label>\n <Input\n id=\"myb-set-distance\"\n type=\"number\"\n inputMode=\"numeric\"\n min={0}\n value={logForm.distanceM}\n onChange={(e) => setLogForm((f) => ({ ...f, distanceM: e.target.value }))}\n placeholder=\"Optional\"\n />\n </div>\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-set-duration\">Duration (sec)</Label>\n <Input\n id=\"myb-set-duration\"\n type=\"number\"\n inputMode=\"numeric\"\n min={0}\n value={logForm.durationSec}\n onChange={(e) => setLogForm((f) => ({ ...f, durationSec: e.target.value }))}\n placeholder=\"Optional\"\n />\n </div>\n </div>\n\n <div className=\"flex items-center justify-between gap-3 rounded-xl bg-bg-sunken/60 p-3\">\n <div className=\"flex items-center gap-2\">\n {effectiveIsPersonalRecord ? (\n <Trophy className=\"h-4 w-4 text-status-success-text\" />\n ) : (\n <Gauge className=\"h-4 w-4 text-accent\" />\n )}\n <div>\n <Label htmlFor=\"myb-set-pr\" className=\"cursor-pointer\">\n Personal record\n </Label>\n <p className=\"text-xs text-text-tertiary\">\n {bestSet.data\n ? `Auto-detected against your best: ${formatNumber(bestSet.data.reps)} × ${formatWeight(bestSet.data.weight)}.`\n : logForm.exerciseName.trim()\n ? 'First time logging this exercise — nothing to compare yet.'\n : 'Flag this set as a new PR.'}\n </p>\n </div>\n </div>\n <Switch\n id=\"myb-set-pr\"\n checked={effectiveIsPersonalRecord}\n onCheckedChange={(v) => {\n setPrTouched(true);\n setLogForm((f) => ({ ...f, isPersonalRecord: v }));\n }}\n />\n </div>\n\n {logError && <p className=\"text-sm text-status-error-text\">{logError}</p>}\n </div>\n\n <ResponsiveDialogFooter>\n <Button variant=\"outline\" onClick={() => setLogOpen(false)}>\n Done\n </Button>\n <Button onClick={submitLog} disabled={logSet.isPending}>\n {logSet.isPending ? 'Logging…' : 'Log set'}\n </Button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n\n {/* Complete session dialog */}\n <ResponsiveDialog open={completeOpen} onOpenChange={setCompleteOpen}>\n <ResponsiveDialogContent>\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>Complete session</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n Wrap up this workout and record how it went.\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n\n <div className=\"space-y-4 px-1\">\n {sets.length === 0 && (\n <div className=\"flex items-start gap-2.5 rounded-xl border border-status-warning-text/40 bg-status-warning-bg-subtle p-3\">\n <AlertTriangle className=\"mt-0.5 h-4 w-4 shrink-0 text-status-warning-text\" />\n <p className=\"text-sm text-text-secondary\">\n <span className=\"font-medium text-text-primary\">No sets logged.</span> You can\n still complete this session, but nothing was recorded for it.\n </p>\n </div>\n )}\n <div className=\"grid grid-cols-2 gap-3\">\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-complete-calories\">Calories burned</Label>\n <Input\n id=\"myb-complete-calories\"\n type=\"number\"\n inputMode=\"numeric\"\n min={0}\n value={completeForm.caloriesBurned}\n onChange={(e) =>\n setCompleteForm((f) => ({ ...f, caloriesBurned: e.target.value }))\n }\n placeholder=\"Optional\"\n />\n </div>\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-complete-rpe\">Perceived exertion (1–10)</Label>\n <Input\n id=\"myb-complete-rpe\"\n type=\"number\"\n inputMode=\"numeric\"\n min={1}\n max={10}\n value={completeForm.perceivedExertion}\n onChange={(e) =>\n setCompleteForm((f) => ({ ...f, perceivedExertion: e.target.value }))\n }\n placeholder=\"Optional\"\n />\n </div>\n </div>\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-complete-notes\">Notes</Label>\n <Textarea\n id=\"myb-complete-notes\"\n rows={3}\n value={completeForm.notes}\n onChange={(e) => setCompleteForm((f) => ({ ...f, notes: e.target.value }))}\n placeholder=\"How did the session feel?\"\n />\n </div>\n\n {completeError && <p className=\"text-sm text-status-error-text\">{completeError}</p>}\n </div>\n\n <ResponsiveDialogFooter>\n <Button variant=\"outline\" onClick={() => setCompleteOpen(false)}>\n Cancel\n </Button>\n <Button onClick={submitComplete} disabled={completeSession.isPending}>\n {completeSession.isPending ? 'Completing…' : 'Complete session'}\n </Button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"SessionDetailPage.js","names":[],"sources":["../../../../src/mybodyshield/pages/train/SessionDetailPage.tsx"],"sourcesContent":["/**\n * Train — workout session detail & live logging.\n * @module mybodyshield/pages/train\n *\n * Renders a single workout session (real `mybSession`, including its `sets`).\n * While a session is IN_PROGRESS you can log sets and complete it; completed /\n * scheduled sessions render read-only. Sets are grouped by exercise. No mock\n * data — everything comes from the myb* hooks.\n */\n\nimport { useCallback, useEffect, useMemo, useState } from 'react';\nimport { motion, useReducedMotion } from 'framer-motion';\nimport { useParams } from 'react-router';\nimport {\n ArrowLeft,\n Plus,\n CheckCircle2,\n Dumbbell,\n Weight,\n Timer,\n Flame,\n Ruler,\n Gauge,\n AlertTriangle,\n ChevronLeft,\n ChevronRight,\n ListChecks,\n Trophy,\n WifiOff,\n X,\n} from 'lucide-react';\nimport {\n Button,\n GlassCard,\n Label,\n Input,\n Textarea,\n Switch,\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n ResponsiveDialogDescription,\n ResponsiveDialogFooter,\n} from '@burdenoff/fe-libs/ui';\nimport {\n PageLayout,\n MetricsCard,\n StatusBadge,\n EmptyState,\n LoadingView,\n ErrorView,\n MybDiscussion,\n} from '../../components';\nimport { IconChip } from '../_shared/visuals';\nimport { useMyBodyShield } from '../../context';\nimport {\n useMybSession,\n useMybLogSet,\n useMybCompleteSession,\n useMybWorkoutExercises,\n useMybExerciseBestSet,\n useMyBodyShieldAnalytics,\n} from '../../hooks';\nimport {\n listPendingActionsForSession,\n type PendingSessionAction,\n} from '../../lib/offlineSessionQueue';\nimport { useOfflineSessionSync } from '../../lib/useOfflineSessionSync';\nimport {\n formatDateTime,\n formatRelativeTime,\n formatNumber,\n formatWeight,\n formatKcal,\n formatDuration,\n formatEnumLabel,\n} from '../../utils/formatters';\nimport type { MybSessionSet, MybExercise } from '../../types';\n\n/**\n * Heuristic for \"this failure means we're offline, not that the request was\n * rejected\" (BOFF-4219). `graphqlFetch`'s underlying `fetch()` throws a\n * `TypeError` when the network is unreachable (no HTTP response at all);\n * a real GraphQL/backend error instead resolves with an `errors` array that\n * `mybQuery` turns into a plain `Error` with a server-authored message, so\n * checking `navigator.onLine` first and falling back to the error shape\n * keeps a genuine validation error (e.g. a deleted session) from being\n * mistaken for a connectivity gap and silently queued.\n */\nfunction isLikelyOffline(err: unknown): boolean {\n if (typeof navigator !== 'undefined' && !navigator.onLine) return true;\n if (err instanceof TypeError) return true;\n const msg = err instanceof Error ? err.message.toLowerCase() : '';\n return msg.includes('failed to fetch') || msg.includes('load failed') || msg.includes('network');\n}\n\n/** Render a queued (not-yet-synced) set the same shape as a real MybSessionSet for display. */\nfunction pendingActionToDisplaySet(action: PendingSessionAction, sessionId: string): MybSessionSet {\n const input = action.input as {\n exerciseName: string;\n setNumber?: number | null;\n reps?: number | null;\n weight?: number | null;\n distanceM?: number | null;\n durationSec?: number | null;\n isPersonalRecord?: boolean | null;\n };\n return {\n id: `pending-${action.clientActionId}`,\n workspaceId: '',\n sessionId,\n exerciseName: input.exerciseName,\n setNumber: input.setNumber ?? 1,\n reps: input.reps ?? 0,\n weight: input.weight ?? 0,\n distanceM: input.distanceM ?? 0,\n durationSec: input.durationSec ?? 0,\n isPersonalRecord: Boolean(input.isPersonalRecord),\n createdAt: action.capturedAt,\n updatedAt: action.capturedAt,\n };\n}\n\n/** Rest timer default when a set isn't part of a guided plan exercise. */\nconst DEFAULT_REST_SECONDS = 90;\n\n/** Stable empty-array reference so the \"no plan\" case never changes identity across renders. */\nconst NO_EXERCISES: readonly MybExercise[] = [];\n\n/**\n * Does a newly-logged `weight`/`reps` beat the member's prior best for this\n * exercise? Mirrors the backend's own ranking (weight desc, then reps desc).\n * `null`/`undefined` best (never logged before) is NOT treated as a record —\n * there is nothing yet to break — matching `mybExerciseBestSet`'s contract.\n */\nfunction beatsBest(weight: number, reps: number, best: MybSessionSet | null | undefined): boolean {\n if (!best) return false;\n if (weight > best.weight) return true;\n if (weight === best.weight && reps > best.reps) return true;\n return false;\n}\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/** Stagger container + rise-in child variants for grouped card entrances. */\nconst STAGGER_CONTAINER = {\n hidden: {},\n show: { transition: { staggerChildren: 0.06 } },\n};\nconst RISE_ITEM = {\n hidden: { opacity: 0, y: 12 },\n show: { opacity: 1, y: 0, transition: { duration: 0.35, ease: MOTION_EASE } },\n};\n\n/** Parse an optional numeric string into a number, or undefined when blank. */\nfunction numOrUndef(value: string): number | undefined {\n const trimmed = value.trim();\n if (!trimmed) return undefined;\n const n = Number(trimmed);\n return Number.isFinite(n) ? n : undefined;\n}\n\ninterface LogSetForm {\n exerciseName: string;\n setNumber: string;\n reps: string;\n weight: string;\n distanceM: string;\n durationSec: string;\n isPersonalRecord: boolean;\n}\n\nconst EMPTY_LOG_FORM: LogSetForm = {\n exerciseName: '',\n setNumber: '',\n reps: '',\n weight: '',\n distanceM: '',\n durationSec: '',\n isPersonalRecord: false,\n};\n\ninterface CompleteForm {\n caloriesBurned: string;\n perceivedExertion: string;\n notes: string;\n}\n\nconst EMPTY_COMPLETE_FORM: CompleteForm = { caloriesBurned: '', perceivedExertion: '', notes: '' };\n\nexport function SessionDetailPage() {\n const { navigateTo, workspaceId } = useMyBodyShield();\n const reduce = useReducedMotion();\n const { id } = useParams<{ id: string }>();\n const analytics = useMyBodyShieldAnalytics();\n\n const session = useMybSession(id);\n const logSet = useMybLogSet();\n const completeSession = useMybCompleteSession();\n const exercisesQuery = useMybWorkoutExercises(session.data?.workoutId ?? undefined);\n\n // BOFF-4219: offline-first queue for logging sets / completing a session\n // that was started while online but hits a connectivity gap mid-workout.\n const offlineSync = useOfflineSessionSync(\n (input) => logSet.mutateAsync(input),\n (vars) => completeSession.mutateAsync(vars),\n () => session.refetch()\n );\n const [pendingActions, setPendingActions] = useState<PendingSessionAction[]>([]);\n const refreshPendingForSession = useCallback(() => {\n if (!id) return;\n void listPendingActionsForSession(id).then(setPendingActions);\n }, [id]);\n useEffect(() => {\n refreshPendingForSession();\n }, [refreshPendingForSession, offlineSync.pendingCount]);\n const pendingSets = useMemo(\n () =>\n id\n ? pendingActions\n .filter((a) => a.type === 'LOG_SET')\n .map((a) => pendingActionToDisplaySet(a, id))\n : [],\n [pendingActions, id]\n );\n const pendingComplete = pendingActions.some((a) => a.type === 'COMPLETE_SESSION');\n\n const [logOpen, setLogOpen] = useState(false);\n const [logForm, setLogForm] = useState<LogSetForm>(EMPTY_LOG_FORM);\n const [logError, setLogError] = useState<string | null>(null);\n const [prTouched, setPrTouched] = useState(false);\n\n const [completeOpen, setCompleteOpen] = useState(false);\n const [completeForm, setCompleteForm] = useState<CompleteForm>(EMPTY_COMPLETE_FORM);\n const [completeError, setCompleteError] = useState<string | null>(null);\n\n // Guided next-exercise stepper (BOFF-4096): `null` means \"auto-follow\" —\n // the active step tracks the first not-yet-fully-logged planned exercise;\n // an explicit index means the member manually paged away from that.\n const [manualExerciseIdx, setManualExerciseIdx] = useState<number | null>(null);\n const [prevWorkoutId, setPrevWorkoutId] = useState<string | null | undefined>(\n session.data?.workoutId\n );\n\n // Rest timer (BOFF-4096): a wall-clock deadline rather than a plain\n // countdown-in-state, so the remaining time stays correct even if the tab\n // was backgrounded and the 1s interval was throttled/paused.\n const [restEndsAt, setRestEndsAt] = useState<number | null>(null);\n const [restRemaining, setRestRemaining] = useState(0);\n\n const bestSet = useMybExerciseBestSet(logForm.exerciseName);\n\n const data = session.data;\n // BOFF-4219: sets queued offline aren't in the server-fetched `data.sets`\n // yet — merge them in so a member sees what they logged immediately,\n // even before the queue has had a chance to sync.\n const sets = useMemo<MybSessionSet[]>(\n () => [...(data?.sets ?? []), ...pendingSets],\n [data?.sets, pendingSets]\n );\n\n const groups = useMemo(() => {\n const map = new Map<string, MybSessionSet[]>();\n for (const st of sets) {\n const arr = map.get(st.exerciseName) ?? [];\n arr.push(st);\n map.set(st.exerciseName, arr);\n }\n return Array.from(map.entries()).map(([name, list]) => ({\n name,\n sets: list.slice().sort((a, b) => a.setNumber - b.setNumber),\n }));\n }, [sets]);\n\n const totalVolume = useMemo(() => sets.reduce((acc, st) => acc + st.reps * st.weight, 0), [sets]);\n\n const startedAt = data?.startedAt;\n const completedAt = data?.completedAt;\n const durationSec = useMemo(() => {\n if (!startedAt || !completedAt) return undefined;\n const start = new Date(startedAt).getTime();\n const end = new Date(completedAt).getTime();\n if (!Number.isFinite(start) || !Number.isFinite(end) || end < start) return undefined;\n return (end - start) / 1000;\n }, [startedAt, completedAt]);\n\n const exercises = exercisesQuery.data ?? NO_EXERCISES;\n\n const loggedCountByExercise = useMemo(() => {\n const map = new Map<string, number>();\n for (const g of groups) map.set(g.name, g.sets.length);\n return map;\n }, [groups]);\n\n const autoExerciseIdx = useMemo(() => {\n if (exercises.length === 0) return 0;\n const idx = exercises.findIndex(\n (ex) => (loggedCountByExercise.get(ex.name) ?? 0) < Math.max(ex.sets, 1)\n );\n return idx === -1 ? exercises.length - 1 : idx;\n }, [exercises, loggedCountByExercise]);\n\n const activeExerciseIdx = Math.min(\n manualExerciseIdx ?? autoExerciseIdx,\n Math.max(exercises.length - 1, 0)\n );\n const activeExercise: MybExercise | undefined = exercises[activeExerciseIdx];\n const allExercisesDone =\n exercises.length > 0 &&\n exercises.every((ex) => (loggedCountByExercise.get(ex.name) ?? 0) >= Math.max(ex.sets, 1));\n\n // A different session's plan invalidates any manual stepper override. This\n // adjusts state during render (React's documented alternative to an\n // effect for \"reset state when a prop changes\") rather than an effect, so\n // it never causes the extra reset-then-settle render an effect would.\n if (session.data?.workoutId !== prevWorkoutId) {\n setPrevWorkoutId(session.data?.workoutId);\n setManualExerciseIdx(null);\n }\n\n // Tick the rest-timer countdown against the wall-clock deadline. The\n // effect only *subscribes* to the interval — every state update happens\n // inside the (deferred) tick callback, never synchronously in the effect\n // body itself, so starting/stopping the timer never forces an extra render.\n useEffect(() => {\n if (restEndsAt == null) return;\n const interval = window.setInterval(() => {\n const remaining = Math.max(0, Math.ceil((restEndsAt - Date.now()) / 1000));\n setRestRemaining(remaining);\n if (remaining <= 0) setRestEndsAt(null);\n }, 1000);\n return () => window.clearInterval(interval);\n }, [restEndsAt]);\n\n // Live auto-PR detection (BOFF-4096): derived at render time, not synced\n // via an effect — whether the in-progress reps/weight would beat the\n // member's historical best, unless they've manually overridden the switch\n // for this exercise.\n const weightNum = numOrUndef(logForm.weight) ?? 0;\n const repsNum = numOrUndef(logForm.reps) ?? 0;\n const autoPredictedPR =\n logForm.exerciseName.trim().length > 0 && beatsBest(weightNum, repsNum, bestSet.data);\n const effectiveIsPersonalRecord = prTouched ? logForm.isPersonalRecord : autoPredictedPR;\n\n if (!id) {\n return (\n <PageLayout title=\"Workout session\">\n <ErrorView message=\"No session was specified.\" onRetry={() => navigateTo('train')} />\n </PageLayout>\n );\n }\n\n if (session.isLoading) {\n return (\n <PageLayout title=\"Workout session\">\n <LoadingView label=\"Loading session…\" />\n </PageLayout>\n );\n }\n\n if (session.isError || !data) {\n return (\n <PageLayout title=\"Workout session\">\n <ErrorView\n title=\"Session not found\"\n message=\"This workout session may have been removed, or you don't have access to it.\"\n onRetry={() => session.refetch()}\n />\n </PageLayout>\n );\n }\n\n const isLive = data.status === 'IN_PROGRESS';\n\n const summary = data.completedAt\n ? `Completed ${formatRelativeTime(data.completedAt)}`\n : data.startedAt\n ? `Started ${formatRelativeTime(data.startedAt)}`\n : data.scheduledFor\n ? `Scheduled ${formatRelativeTime(data.scheduledFor)}`\n : `Created ${formatRelativeTime(data.createdAt)}`;\n\n const openLog = () => {\n setLogForm(EMPTY_LOG_FORM);\n setLogError(null);\n setPrTouched(false);\n setLogOpen(true);\n };\n\n /** Guided-stepper quick action: pre-fill the log dialog from a planned exercise. */\n const openLogForExercise = (exercise: MybExercise) => {\n const alreadyLogged = loggedCountByExercise.get(exercise.name) ?? 0;\n setLogForm({\n ...EMPTY_LOG_FORM,\n exerciseName: exercise.name,\n setNumber: String(alreadyLogged + 1),\n reps: exercise.reps > 0 ? String(exercise.reps) : '',\n weight: exercise.targetWeight > 0 ? String(exercise.targetWeight) : '',\n });\n setLogError(null);\n setPrTouched(false);\n setLogOpen(true);\n };\n\n const submitLog = async () => {\n setLogError(null);\n const exerciseName = logForm.exerciseName.trim();\n if (!exerciseName) {\n setLogError('Exercise name is required.');\n return;\n }\n const input = {\n exerciseName,\n setNumber: numOrUndef(logForm.setNumber),\n reps: numOrUndef(logForm.reps),\n weight: numOrUndef(logForm.weight),\n distanceM: numOrUndef(logForm.distanceM),\n durationSec: numOrUndef(logForm.durationSec),\n isPersonalRecord: effectiveIsPersonalRecord,\n };\n const advanceForm = (nextSetNumber: number) => {\n setLogForm({ ...EMPTY_LOG_FORM, exerciseName, setNumber: String(nextSetNumber) });\n setPrTouched(false);\n const restSeconds =\n activeExercise && activeExercise.name === exerciseName && activeExercise.restSeconds > 0\n ? activeExercise.restSeconds\n : DEFAULT_REST_SECONDS;\n setRestEndsAt(Date.now() + restSeconds * 1000);\n setRestRemaining(restSeconds);\n };\n // BOFF-4219: log offline-first — don't even attempt the network round-trip\n // when we already know we're offline, so the set is captured instantly.\n if (!offlineSync.isOnline) {\n await offlineSync.enqueueLogSet(id, input);\n refreshPendingForSession();\n advanceForm((numOrUndef(logForm.setNumber) ?? sets.length) + 1);\n return;\n }\n try {\n const created = await logSet.mutateAsync({ sessionId: id, ...input });\n advanceForm(created.setNumber + 1);\n } catch (e) {\n if (isLikelyOffline(e)) {\n await offlineSync.enqueueLogSet(id, input);\n refreshPendingForSession();\n advanceForm((numOrUndef(logForm.setNumber) ?? sets.length) + 1);\n return;\n }\n setLogError(e instanceof Error ? e.message : 'Failed to log the set.');\n }\n };\n\n const openComplete = () => {\n setCompleteForm(EMPTY_COMPLETE_FORM);\n setCompleteError(null);\n setCompleteOpen(true);\n };\n\n const submitComplete = async () => {\n setCompleteError(null);\n const input = {\n caloriesBurned: numOrUndef(completeForm.caloriesBurned),\n perceivedExertion: numOrUndef(completeForm.perceivedExertion),\n notes: completeForm.notes.trim() ? completeForm.notes.trim() : undefined,\n };\n if (!offlineSync.isOnline) {\n await offlineSync.enqueueCompleteSession(id, input);\n refreshPendingForSession();\n setCompleteOpen(false);\n return;\n }\n try {\n await completeSession.mutateAsync({ id, input });\n setCompleteOpen(false);\n } catch (e) {\n if (isLikelyOffline(e)) {\n await offlineSync.enqueueCompleteSession(id, input);\n refreshPendingForSession();\n setCompleteOpen(false);\n return;\n }\n setCompleteError(e instanceof Error ? e.message : 'Failed to complete the session.');\n }\n };\n\n const actions = isLive ? (\n <>\n <Button variant=\"outline\" onClick={openLog}>\n <Plus className=\"h-4 w-4\" /> Log set\n </Button>\n <Button onClick={openComplete} disabled={pendingComplete}>\n <CheckCircle2 className=\"h-4 w-4\" /> {pendingComplete ? 'Pending sync…' : 'Complete'}\n </Button>\n </>\n ) : undefined;\n\n return (\n <PageLayout title=\"Workout session\" description={summary} actions={actions}>\n <button\n type=\"button\"\n onClick={() => navigateTo('train')}\n className=\"mb-4 inline-flex items-center gap-1.5 text-sm font-medium text-text-secondary hover:text-text-primary\"\n >\n <ArrowLeft className=\"h-4 w-4\" /> Back to sessions\n </button>\n\n {/* BOFF-4219: offline queue status — only shown when it's relevant, so\n nothing changes for a member who never leaves connectivity. */}\n {(!offlineSync.isOnline || pendingActions.length > 0) && (\n <div className=\"mb-4 flex items-center gap-2 rounded-xl border border-status-warning-text/40 bg-status-warning-bg-subtle px-4 py-2.5 text-sm text-text-primary\">\n <WifiOff className=\"h-4 w-4 shrink-0 text-status-warning-text\" />\n <span>\n {!offlineSync.isOnline\n ? `You're offline — logged sets and completion will sync automatically once you're back online${\n pendingActions.length > 0 ? ` (${pendingActions.length} queued)` : ''\n }.`\n : `Syncing ${pendingActions.length} queued ${pendingActions.length === 1 ? 'action' : 'actions'}…`}\n </span>\n {offlineSync.isOnline && pendingActions.length > 0 && (\n <Button\n variant=\"ghost\"\n onClick={offlineSync.syncNow}\n className=\"ml-auto h-7 px-2 text-xs\"\n >\n Retry now\n </Button>\n )}\n </div>\n )}\n\n {/* Header card */}\n <motion.div\n initial={reduce ? false : { opacity: 0, y: 10 }}\n animate={{ opacity: 1, y: 0 }}\n transition={reduce ? { duration: 0 } : { duration: 0.35, ease: MOTION_EASE }}\n >\n <GlassCard treatment=\"glass\" glow={isLive} className=\"p-6\">\n <div className=\"flex flex-wrap items-start justify-between gap-3\">\n <div className=\"flex items-center gap-3\">\n <IconChip\n icon={isLive ? <Flame className=\"h-5 w-5\" /> : <Dumbbell className=\"h-5 w-5\" />}\n tint={isLive ? 'var(--color-accent-teal)' : 'var(--color-accent-primary)'}\n />\n <div>\n <div className=\"flex items-center gap-2\">\n <StatusBadge status={data.status} />\n </div>\n <p className=\"mt-1 text-sm text-text-secondary\">{summary}</p>\n </div>\n </div>\n </div>\n\n <div className=\"mt-5 grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n {data.scheduledFor && (\n <div>\n <p className=\"text-xs text-text-tertiary\">Scheduled for</p>\n <p className=\"mt-0.5 text-sm font-medium text-text-primary\">\n {formatDateTime(data.scheduledFor)}\n </p>\n </div>\n )}\n {data.startedAt && (\n <div>\n <p className=\"text-xs text-text-tertiary\">Started</p>\n <p className=\"mt-0.5 text-sm font-medium text-text-primary\">\n {formatDateTime(data.startedAt)}\n </p>\n </div>\n )}\n {data.completedAt && (\n <div>\n <p className=\"text-xs text-text-tertiary\">Completed</p>\n <p className=\"mt-0.5 text-sm font-medium text-text-primary\">\n {formatDateTime(data.completedAt)}\n </p>\n </div>\n )}\n {data.perceivedExertion != null && (\n <div>\n <p className=\"text-xs text-text-tertiary\">Perceived exertion</p>\n <p className=\"mt-0.5 text-sm font-medium tabular-nums text-text-primary\">\n {data.perceivedExertion}/10 RPE\n </p>\n </div>\n )}\n {data.caloriesBurned > 0 && (\n <div>\n <p className=\"text-xs text-text-tertiary\">Calories burned</p>\n <p className=\"mt-0.5 text-sm font-medium tabular-nums text-text-primary\">\n {formatKcal(data.caloriesBurned)}\n </p>\n </div>\n )}\n </div>\n\n {data.notes && (\n <div className=\"mt-5 rounded-xl bg-bg-sunken/60 p-4\">\n <p className=\"text-xs text-text-tertiary\">Notes</p>\n <p className=\"mt-1 whitespace-pre-wrap text-sm text-text-secondary\">{data.notes}</p>\n </div>\n )}\n </GlassCard>\n </motion.div>\n\n {/* Rest timer — appears after a set is logged, counts down to the next one */}\n {isLive && restRemaining > 0 && (\n <motion.div\n initial={reduce ? false : { opacity: 0, y: -6 }}\n animate={{ opacity: 1, y: 0 }}\n transition={reduce ? { duration: 0 } : { duration: 0.25, ease: MOTION_EASE }}\n className=\"mt-4\"\n >\n <GlassCard\n treatment=\"plain\"\n className=\"flex flex-wrap items-center justify-between gap-3 border border-accent/30 bg-accent/5 p-4\"\n >\n <div className=\"flex items-center gap-2.5\">\n <IconChip icon={<Timer className=\"h-4 w-4\" />} tint=\"var(--color-accent-teal)\" />\n <div>\n <p className=\"text-sm font-semibold text-text-primary\">Resting</p>\n <p className=\"text-xs tabular-nums text-text-secondary\">\n {formatDuration(restRemaining)} remaining\n </p>\n </div>\n </div>\n <div className=\"flex items-center gap-2\">\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={() => {\n setRestEndsAt((prev) => (prev ? prev + 15_000 : prev));\n setRestRemaining((r) => r + 15);\n }}\n >\n +15s\n </Button>\n <Button\n variant=\"ghost\"\n size=\"sm\"\n onClick={() => {\n setRestEndsAt(null);\n setRestRemaining(0);\n }}\n >\n <X className=\"h-4 w-4\" /> Skip\n </Button>\n </div>\n </GlassCard>\n </motion.div>\n )}\n\n {/* Guided next-exercise stepper — only for sessions tied to a planned workout */}\n {isLive && exercises.length > 0 && (\n <motion.div\n initial={reduce ? false : { opacity: 0, y: 10 }}\n animate={{ opacity: 1, y: 0 }}\n transition={reduce ? { duration: 0 } : { duration: 0.35, ease: MOTION_EASE, delay: 0.05 }}\n className=\"mt-4\"\n >\n <GlassCard treatment=\"plain\" className=\"p-4\">\n <div className=\"mb-3 flex items-center justify-between gap-2\">\n <div className=\"flex items-center gap-2.5\">\n <IconChip icon={<ListChecks className=\"h-4 w-4\" />} />\n <div>\n <h3 className=\"text-sm font-semibold text-text-primary\">Guided workout</h3>\n <p className=\"text-xs text-text-secondary\">\n {allExercisesDone\n ? 'All planned exercises logged — nice work.'\n : `Exercise ${activeExerciseIdx + 1} of ${exercises.length}`}\n </p>\n </div>\n </div>\n <div className=\"flex items-center gap-1.5\">\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={() => setManualExerciseIdx(Math.max(activeExerciseIdx - 1, 0))}\n disabled={activeExerciseIdx === 0}\n aria-label=\"Previous exercise\"\n >\n <ChevronLeft className=\"h-4 w-4\" />\n </Button>\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={() =>\n setManualExerciseIdx(Math.min(activeExerciseIdx + 1, exercises.length - 1))\n }\n disabled={activeExerciseIdx >= exercises.length - 1}\n aria-label=\"Next exercise\"\n >\n <ChevronRight className=\"h-4 w-4\" />\n </Button>\n </div>\n </div>\n\n {activeExercise && (\n <div className=\"flex flex-wrap items-center justify-between gap-3 rounded-xl bg-bg-sunken/60 p-3\">\n <div>\n <p className=\"text-sm font-semibold text-text-primary\">{activeExercise.name}</p>\n <p className=\"mt-0.5 text-xs text-text-secondary\">\n {formatEnumLabel(activeExercise.muscleGroup)} · {activeExercise.sets} ×{' '}\n {activeExercise.reps} reps\n {activeExercise.targetWeight > 0\n ? ` @ ${formatWeight(activeExercise.targetWeight)}`\n : ''}\n {activeExercise.restSeconds > 0\n ? ` · ${formatDuration(activeExercise.restSeconds)} rest`\n : ''}\n </p>\n <p className=\"mt-0.5 text-xs tabular-nums text-text-tertiary\">\n {loggedCountByExercise.get(activeExercise.name) ?? 0} of{' '}\n {Math.max(activeExercise.sets, 1)} sets logged\n </p>\n </div>\n <Button size=\"sm\" onClick={() => openLogForExercise(activeExercise)}>\n <Plus className=\"h-4 w-4\" /> Log this exercise\n </Button>\n </div>\n )}\n </GlassCard>\n </motion.div>\n )}\n\n {/* Stat cards */}\n <motion.div\n className=\"mt-6 grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4\"\n variants={reduce ? undefined : STAGGER_CONTAINER}\n initial={reduce ? false : 'hidden'}\n animate=\"show\"\n >\n <motion.div variants={reduce ? undefined : RISE_ITEM}>\n <MetricsCard\n icon={\n <span className=\"text-action-primary-bg\">\n <Dumbbell className=\"h-5 w-5\" />\n </span>\n }\n value={formatNumber(sets.length)}\n label=\"Total sets\"\n />\n </motion.div>\n <motion.div variants={reduce ? undefined : RISE_ITEM}>\n <MetricsCard\n icon={\n <span className=\"text-action-primary-bg\">\n <Weight className=\"h-5 w-5\" />\n </span>\n }\n value={`${formatNumber(totalVolume)} kg`}\n label=\"Total volume\"\n />\n </motion.div>\n <motion.div variants={reduce ? undefined : RISE_ITEM}>\n <MetricsCard\n icon={\n <span className=\"text-action-primary-bg\">\n <Timer className=\"h-5 w-5\" />\n </span>\n }\n value={durationSec != null ? formatDuration(durationSec) : '—'}\n label=\"Duration\"\n />\n </motion.div>\n <motion.div variants={reduce ? undefined : RISE_ITEM}>\n <MetricsCard\n icon={\n <span className=\"text-action-primary-bg\">\n <Flame className=\"h-5 w-5\" />\n </span>\n }\n value={data.caloriesBurned > 0 ? formatKcal(data.caloriesBurned) : '—'}\n label=\"Calories burned\"\n />\n </motion.div>\n </motion.div>\n\n {/* Logged sets */}\n <div className=\"mt-8\">\n <div className=\"mb-3 flex items-end justify-between gap-3\">\n <div>\n <h2 className=\"text-base font-semibold text-text-primary\">Logged sets</h2>\n <p className=\"mt-0.5 text-sm text-text-secondary\">\n Every set recorded during this session, grouped by exercise.\n </p>\n </div>\n {isLive && (\n <Button variant=\"outline\" size=\"sm\" onClick={openLog} className=\"shrink-0\">\n <Plus className=\"h-4 w-4\" /> Log set\n </Button>\n )}\n </div>\n\n {groups.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n icon={<Dumbbell className=\"h-10 w-10\" />}\n title=\"No sets logged yet\"\n description={\n isLive\n ? 'Log your first set to start tracking this workout.'\n : 'No sets were recorded for this session.'\n }\n action={\n isLive ? (\n <Button onClick={openLog}>\n <Plus className=\"h-4 w-4\" /> Log set\n </Button>\n ) : undefined\n }\n />\n ) : (\n <motion.div\n className=\"space-y-4\"\n variants={reduce ? undefined : STAGGER_CONTAINER}\n initial={reduce ? false : 'hidden'}\n animate=\"show\"\n >\n {groups.map((group) => {\n const prCount = group.sets.filter((st) => st.isPersonalRecord).length;\n return (\n <motion.div key={group.name} variants={reduce ? undefined : RISE_ITEM}>\n <GlassCard treatment=\"plain\" className=\"p-4 transition-shadow hover:shadow-float\">\n <div className=\"mb-3 flex items-center justify-between gap-2\">\n <div className=\"flex min-w-0 items-center gap-2.5\">\n <IconChip icon={<Dumbbell className=\"h-4 w-4\" />} />\n <h3 className=\"truncate text-sm font-semibold text-text-primary\">\n {group.name}\n </h3>\n {prCount > 0 && (\n <span className=\"inline-flex items-center rounded-full bg-status-success-bg-subtle px-2 py-0.5 text-[11px] font-semibold text-status-success-text shadow-[0_0_0_3px_color-mix(in_oklab,var(--color-status-success-text)_14%,transparent)]\">\n {prCount} PR{prCount === 1 ? '' : 's'}\n </span>\n )}\n </div>\n <span className=\"shrink-0 text-xs tabular-nums text-text-tertiary\">\n {formatNumber(group.sets.length)} {group.sets.length === 1 ? 'set' : 'sets'}\n </span>\n </div>\n <ul className=\"divide-y divide-border-seam\">\n {group.sets.map((st) => {\n const isCardio = st.durationSec > 0 || st.distanceM > 0;\n return (\n <li\n key={st.id}\n className={\n 'flex flex-wrap items-center justify-between gap-2 rounded-lg px-2 py-2 transition-colors' +\n (st.isPersonalRecord\n ? ' bg-[color-mix(in_oklab,var(--color-status-success-text)_9%,transparent)]'\n : ' hover:bg-bg-sunken/40')\n }\n >\n <div className=\"flex items-center gap-2\">\n <span className=\"text-sm font-medium text-text-primary tabular-nums\">\n Set {st.setNumber}: {formatNumber(st.reps)} ×{' '}\n {formatWeight(st.weight)}\n </span>\n {st.isPersonalRecord && (\n <span className=\"inline-flex rounded-full shadow-[0_0_0_3px_color-mix(in_oklab,var(--color-status-success-text)_16%,transparent)]\">\n <StatusBadge status=\"PR\" />\n </span>\n )}\n </div>\n <div className=\"flex flex-wrap items-center gap-x-3 gap-y-1 text-xs text-text-secondary\">\n {isCardio && st.durationSec > 0 && (\n <span className=\"inline-flex items-center gap-1 tabular-nums\">\n <Timer className=\"h-3.5 w-3.5 text-accent\" />\n {formatDuration(st.durationSec)}\n </span>\n )}\n {isCardio && st.distanceM > 0 && (\n <span className=\"inline-flex items-center gap-1 tabular-nums\">\n <Ruler className=\"h-3.5 w-3.5 text-accent\" />\n {formatNumber(st.distanceM)} m\n </span>\n )}\n </div>\n </li>\n );\n })}\n </ul>\n </GlassCard>\n </motion.div>\n );\n })}\n </motion.div>\n )}\n </div>\n\n {/* Coach feedback / discussion — available for any session */}\n <div className=\"mt-8\">\n <MybDiscussion\n entityType=\"mybodyshield-session\"\n entityId={data.id}\n title=\"Coach feedback\"\n description=\"Discuss form, cues and this session's results with your coach and team.\"\n />\n </div>\n\n {/* Log set dialog */}\n <ResponsiveDialog open={logOpen} onOpenChange={setLogOpen}>\n <ResponsiveDialogContent>\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>Log a set</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n Record reps and load for strength work, or duration and distance for cardio. The\n dialog stays open so you can log sets back-to-back.\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n\n <div className=\"space-y-4 px-1\">\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-set-exercise\">Exercise</Label>\n <Input\n id=\"myb-set-exercise\"\n value={logForm.exerciseName}\n onChange={(e) => {\n const exerciseName = e.target.value;\n setLogForm((f) => ({ ...f, exerciseName }));\n // A different exercise has a different history — let auto-PR\n // detection recompute fresh instead of keeping a stale override.\n setPrTouched(false);\n }}\n placeholder=\"e.g. Back Squat\"\n />\n </div>\n\n <div className=\"grid grid-cols-2 gap-3\">\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-set-number\">Set number</Label>\n <Input\n id=\"myb-set-number\"\n type=\"number\"\n inputMode=\"numeric\"\n min={1}\n value={logForm.setNumber}\n onChange={(e) => setLogForm((f) => ({ ...f, setNumber: e.target.value }))}\n placeholder=\"1\"\n />\n </div>\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-set-reps\">Reps</Label>\n <Input\n id=\"myb-set-reps\"\n type=\"number\"\n inputMode=\"numeric\"\n min={0}\n value={logForm.reps}\n onChange={(e) => setLogForm((f) => ({ ...f, reps: e.target.value }))}\n placeholder=\"8\"\n />\n </div>\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-set-weight\">Weight (kg)</Label>\n <Input\n id=\"myb-set-weight\"\n type=\"number\"\n inputMode=\"decimal\"\n min={0}\n step=\"0.5\"\n value={logForm.weight}\n onChange={(e) => setLogForm((f) => ({ ...f, weight: e.target.value }))}\n placeholder=\"60\"\n />\n </div>\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-set-distance\">Distance (m)</Label>\n <Input\n id=\"myb-set-distance\"\n type=\"number\"\n inputMode=\"numeric\"\n min={0}\n value={logForm.distanceM}\n onChange={(e) => setLogForm((f) => ({ ...f, distanceM: e.target.value }))}\n placeholder=\"Optional\"\n />\n </div>\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-set-duration\">Duration (sec)</Label>\n <Input\n id=\"myb-set-duration\"\n type=\"number\"\n inputMode=\"numeric\"\n min={0}\n value={logForm.durationSec}\n onChange={(e) => setLogForm((f) => ({ ...f, durationSec: e.target.value }))}\n placeholder=\"Optional\"\n />\n </div>\n </div>\n\n <div className=\"flex items-center justify-between gap-3 rounded-xl bg-bg-sunken/60 p-3\">\n <div className=\"flex items-center gap-2\">\n {effectiveIsPersonalRecord ? (\n <Trophy className=\"h-4 w-4 text-status-success-text\" />\n ) : (\n <Gauge className=\"h-4 w-4 text-accent\" />\n )}\n <div>\n <Label htmlFor=\"myb-set-pr\" className=\"cursor-pointer\">\n Personal record\n </Label>\n <p className=\"text-xs text-text-tertiary\">\n {bestSet.data\n ? `Auto-detected against your best: ${formatNumber(bestSet.data.reps)} × ${formatWeight(bestSet.data.weight)}.`\n : logForm.exerciseName.trim()\n ? 'First time logging this exercise — nothing to compare yet.'\n : 'Flag this set as a new PR.'}\n </p>\n </div>\n </div>\n <Switch\n id=\"myb-set-pr\"\n checked={effectiveIsPersonalRecord}\n onCheckedChange={(v) => {\n setPrTouched(true);\n setLogForm((f) => ({ ...f, isPersonalRecord: v }));\n }}\n />\n </div>\n\n {logError && <p className=\"text-sm text-status-error-text\">{logError}</p>}\n </div>\n\n <ResponsiveDialogFooter>\n <Button variant=\"outline\" onClick={() => setLogOpen(false)}>\n Done\n </Button>\n <Button onClick={submitLog} disabled={logSet.isPending}>\n {logSet.isPending ? 'Logging…' : 'Log set'}\n </Button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n\n {/* Complete session dialog */}\n <ResponsiveDialog open={completeOpen} onOpenChange={setCompleteOpen}>\n <ResponsiveDialogContent>\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>Complete session</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n Wrap up this workout and record how it went.\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n\n <div className=\"space-y-4 px-1\">\n {sets.length === 0 && (\n <div className=\"flex items-start gap-2.5 rounded-xl border border-status-warning-text/40 bg-status-warning-bg-subtle p-3\">\n <AlertTriangle className=\"mt-0.5 h-4 w-4 shrink-0 text-status-warning-text\" />\n <p className=\"text-sm text-text-secondary\">\n <span className=\"font-medium text-text-primary\">No sets logged.</span> You can\n still complete this session, but nothing was recorded for it.\n </p>\n </div>\n )}\n <div className=\"grid grid-cols-2 gap-3\">\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-complete-calories\">Calories burned</Label>\n <Input\n id=\"myb-complete-calories\"\n type=\"number\"\n inputMode=\"numeric\"\n min={0}\n value={completeForm.caloriesBurned}\n onChange={(e) =>\n setCompleteForm((f) => ({ ...f, caloriesBurned: e.target.value }))\n }\n placeholder=\"Optional\"\n />\n </div>\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-complete-rpe\">Perceived exertion (1–10)</Label>\n <Input\n id=\"myb-complete-rpe\"\n type=\"number\"\n inputMode=\"numeric\"\n min={1}\n max={10}\n value={completeForm.perceivedExertion}\n onChange={(e) =>\n setCompleteForm((f) => ({ ...f, perceivedExertion: e.target.value }))\n }\n placeholder=\"Optional\"\n />\n </div>\n </div>\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-complete-notes\">Notes</Label>\n <Textarea\n id=\"myb-complete-notes\"\n rows={3}\n value={completeForm.notes}\n onChange={(e) => setCompleteForm((f) => ({ ...f, notes: e.target.value }))}\n placeholder=\"How did the session feel?\"\n />\n </div>\n\n {completeError && <p className=\"text-sm text-status-error-text\">{completeError}</p>}\n </div>\n\n <ResponsiveDialogFooter>\n <Button variant=\"outline\" onClick={() => setCompleteOpen(false)}>\n Cancel\n </Button>\n <Button onClick={submitComplete} disabled={completeSession.isPending}>\n {completeSession.isPending ? 'Completing…' : 'Complete session'}\n </Button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@burdenoff/microfe-mybodyshield",
|
|
3
|
-
"version": "2026.
|
|
3
|
+
"version": "2026.801.1",
|
|
4
4
|
"description": "Burdenoff MyBodyShield Microfrontend — health & fitness (training, nutrition, progress, wellbeing)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
},
|
|
49
49
|
"dependencies": {
|
|
50
50
|
"@apollo/client": "^4.0.11",
|
|
51
|
-
"@burdenoff/fe-libs": "2026.
|
|
51
|
+
"@burdenoff/fe-libs": "2026.801.1",
|
|
52
52
|
"@emotion/is-prop-valid": "^1.4.0",
|
|
53
53
|
"@tanstack/react-query": "^5.90.16",
|
|
54
54
|
"clsx": "^2.1.1",
|
|
@@ -57,7 +57,7 @@
|
|
|
57
57
|
"lucide-react": "^0.468.0",
|
|
58
58
|
"react": "^19.0.0",
|
|
59
59
|
"react-dom": "^19.0.0",
|
|
60
|
-
"react-router
|
|
60
|
+
"react-router": "^8.3.0",
|
|
61
61
|
"recharts": "^2.15.0",
|
|
62
62
|
"tailwind-merge": "^3.4.0",
|
|
63
63
|
"zustand": "^5.0.3"
|