@burdenoff/microfe-mybodyshield 2026.706.2 → 2026.713.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/dev/main.d.ts +2 -0
- package/dist/dev/main.d.ts.map +1 -0
- package/dist/generated/global-operations.d.ts +2 -0
- package/dist/generated/global-operations.d.ts.map +1 -0
- package/dist/generated/global-types.d.ts +18199 -0
- package/dist/generated/global-types.d.ts.map +1 -0
- package/dist/generated/wspace-operations.d.ts +2 -0
- package/dist/generated/wspace-operations.d.ts.map +1 -0
- package/dist/generated/wspace-types.d.ts +43046 -0
- package/dist/generated/wspace-types.d.ts.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -1
- package/dist/mybodyshield/MyBodyShieldRoot.d.ts +5 -0
- package/dist/mybodyshield/MyBodyShieldRoot.d.ts.map +1 -0
- package/dist/mybodyshield/MyBodyShieldRoutes.d.ts +4 -0
- package/dist/mybodyshield/MyBodyShieldRoutes.d.ts.map +1 -0
- package/dist/mybodyshield/MyBodyShieldRoutes.js +77 -36
- package/dist/mybodyshield/MyBodyShieldRoutes.js.map +1 -1
- package/dist/mybodyshield/components/DataTable.d.ts +28 -0
- package/dist/mybodyshield/components/DataTable.d.ts.map +1 -0
- package/dist/mybodyshield/components/EmptyState.d.ts +18 -0
- package/dist/mybodyshield/components/EmptyState.d.ts.map +1 -0
- package/dist/mybodyshield/components/EvidenceUpload.d.ts +14 -0
- package/dist/mybodyshield/components/EvidenceUpload.d.ts.map +1 -0
- package/dist/mybodyshield/components/FeatureFlagGuard.d.ts +11 -0
- package/dist/mybodyshield/components/FeatureFlagGuard.d.ts.map +1 -0
- package/dist/mybodyshield/components/FeatureFlagGuard.js +27 -0
- package/dist/mybodyshield/components/FeatureFlagGuard.js.map +1 -0
- package/dist/mybodyshield/components/FileLink.d.ts +15 -0
- package/dist/mybodyshield/components/FileLink.d.ts.map +1 -0
- package/dist/mybodyshield/components/FilterTabs.d.ts +18 -0
- package/dist/mybodyshield/components/FilterTabs.d.ts.map +1 -0
- package/dist/mybodyshield/components/MetricsCard.d.ts +23 -0
- package/dist/mybodyshield/components/MetricsCard.d.ts.map +1 -0
- package/dist/mybodyshield/components/MiniBar.d.ts +16 -0
- package/dist/mybodyshield/components/MiniBar.d.ts.map +1 -0
- package/dist/mybodyshield/components/MybDiscussion.d.ts +18 -0
- package/dist/mybodyshield/components/MybDiscussion.d.ts.map +1 -0
- package/dist/mybodyshield/components/MybDiscussion.js +5 -5
- package/dist/mybodyshield/components/MybTags.d.ts +17 -0
- package/dist/mybodyshield/components/MybTags.d.ts.map +1 -0
- package/dist/mybodyshield/components/PageDisabled.d.ts +9 -0
- package/dist/mybodyshield/components/PageDisabled.d.ts.map +1 -0
- package/dist/mybodyshield/components/PageDisabled.js +17 -0
- package/dist/mybodyshield/components/PageDisabled.js.map +1 -0
- package/dist/mybodyshield/components/PageLayout.d.ts +16 -0
- package/dist/mybodyshield/components/PageLayout.d.ts.map +1 -0
- package/dist/mybodyshield/components/Pagination.d.ts +22 -0
- package/dist/mybodyshield/components/Pagination.d.ts.map +1 -0
- package/dist/mybodyshield/components/StateViews.d.ts +25 -0
- package/dist/mybodyshield/components/StateViews.d.ts.map +1 -0
- package/dist/mybodyshield/components/StateViews.js +2 -1
- package/dist/mybodyshield/components/StateViews.js.map +1 -1
- package/dist/mybodyshield/components/StatusBadge.d.ts +9 -0
- package/dist/mybodyshield/components/StatusBadge.d.ts.map +1 -0
- package/dist/mybodyshield/components/Tabs.d.ts +17 -0
- package/dist/mybodyshield/components/Tabs.d.ts.map +1 -0
- package/dist/mybodyshield/components/UserSearchSelect.d.ts +28 -0
- package/dist/mybodyshield/components/UserSearchSelect.d.ts.map +1 -0
- package/dist/mybodyshield/components/index.d.ts +22 -0
- package/dist/mybodyshield/components/index.d.ts.map +1 -0
- package/dist/mybodyshield/components/index.js +4 -2
- package/dist/mybodyshield/context/MyBodyShieldContext.d.ts +16 -0
- package/dist/mybodyshield/context/MyBodyShieldContext.d.ts.map +1 -0
- package/dist/mybodyshield/context/index.d.ts +6 -0
- package/dist/mybodyshield/context/index.d.ts.map +1 -0
- package/dist/mybodyshield/hooks/index.d.ts +12 -0
- package/dist/mybodyshield/hooks/index.d.ts.map +1 -0
- package/dist/mybodyshield/hooks/index.js +1 -0
- package/dist/mybodyshield/hooks/useMyBodyShieldAnalytics.d.ts +21 -0
- package/dist/mybodyshield/hooks/useMyBodyShieldAnalytics.d.ts.map +1 -0
- package/dist/mybodyshield/hooks/useMyBodyShieldAnalytics.js +35 -0
- package/dist/mybodyshield/hooks/useMyBodyShieldAnalytics.js.map +1 -0
- package/dist/mybodyshield/hooks/useMyBodyShieldApi.d.ts +83 -0
- package/dist/mybodyshield/hooks/useMyBodyShieldApi.d.ts.map +1 -0
- package/dist/mybodyshield/hooks/useMybComments.d.ts +36 -0
- package/dist/mybodyshield/hooks/useMybComments.d.ts.map +1 -0
- package/dist/mybodyshield/hooks/useMybFiles.d.ts +47 -0
- package/dist/mybodyshield/hooks/useMybFiles.d.ts.map +1 -0
- package/dist/mybodyshield/hooks/useMybTags.d.ts +44 -0
- package/dist/mybodyshield/hooks/useMybTags.d.ts.map +1 -0
- package/dist/mybodyshield/hooks/useMybUsers.d.ts +27 -0
- package/dist/mybodyshield/hooks/useMybUsers.d.ts.map +1 -0
- package/dist/mybodyshield/hooks/usePager.d.ts +29 -0
- package/dist/mybodyshield/hooks/usePager.d.ts.map +1 -0
- package/dist/mybodyshield/index.d.ts +9 -0
- package/dist/mybodyshield/index.d.ts.map +1 -0
- package/dist/mybodyshield/pages/DashboardPage.d.ts +10 -0
- package/dist/mybodyshield/pages/DashboardPage.d.ts.map +1 -0
- package/dist/mybodyshield/pages/DashboardPage.js +91 -86
- package/dist/mybodyshield/pages/DashboardPage.js.map +1 -1
- package/dist/mybodyshield/pages/_shared/visuals.d.ts +77 -0
- package/dist/mybodyshield/pages/_shared/visuals.d.ts.map +1 -0
- package/dist/mybodyshield/pages/coaching/CoachingPage.d.ts +11 -0
- package/dist/mybodyshield/pages/coaching/CoachingPage.d.ts.map +1 -0
- package/dist/mybodyshield/pages/coaching/CoachingPage.js +60 -60
- package/dist/mybodyshield/pages/goals/GoalsPage.d.ts +11 -0
- package/dist/mybodyshield/pages/goals/GoalsPage.d.ts.map +1 -0
- package/dist/mybodyshield/pages/goals/GoalsPage.js +125 -125
- package/dist/mybodyshield/pages/goals/GoalsPage.js.map +1 -1
- package/dist/mybodyshield/pages/nutrition/NutritionPage.d.ts +13 -0
- package/dist/mybodyshield/pages/nutrition/NutritionPage.d.ts.map +1 -0
- package/dist/mybodyshield/pages/nutrition/NutritionPage.js +129 -129
- package/dist/mybodyshield/pages/nutrition/NutritionPage.js.map +1 -1
- package/dist/mybodyshield/pages/programs/ProgramDetailPage.d.ts +10 -0
- package/dist/mybodyshield/pages/programs/ProgramDetailPage.d.ts.map +1 -0
- package/dist/mybodyshield/pages/programs/ProgramDetailPage.js +218 -191
- package/dist/mybodyshield/pages/programs/ProgramDetailPage.js.map +1 -1
- package/dist/mybodyshield/pages/programs/ProgramsPage.d.ts +11 -0
- package/dist/mybodyshield/pages/programs/ProgramsPage.d.ts.map +1 -0
- package/dist/mybodyshield/pages/programs/ProgramsPage.js +197 -193
- package/dist/mybodyshield/pages/programs/ProgramsPage.js.map +1 -1
- package/dist/mybodyshield/pages/progress/ProgressPage.d.ts +13 -0
- package/dist/mybodyshield/pages/progress/ProgressPage.d.ts.map +1 -0
- package/dist/mybodyshield/pages/progress/ProgressPage.js +143 -143
- package/dist/mybodyshield/pages/progress/ProgressPage.js.map +1 -1
- package/dist/mybodyshield/pages/recovery/RecoveryPage.d.ts +11 -0
- package/dist/mybodyshield/pages/recovery/RecoveryPage.d.ts.map +1 -0
- package/dist/mybodyshield/pages/recovery/RecoveryPage.js +60 -60
- package/dist/mybodyshield/pages/recovery/RecoveryPage.js.map +1 -1
- package/dist/mybodyshield/pages/train/SessionDetailPage.d.ts +11 -0
- package/dist/mybodyshield/pages/train/SessionDetailPage.d.ts.map +1 -0
- package/dist/mybodyshield/pages/train/SessionDetailPage.js +184 -181
- package/dist/mybodyshield/pages/train/SessionDetailPage.js.map +1 -1
- package/dist/mybodyshield/pages/train/SessionsPage.d.ts +11 -0
- package/dist/mybodyshield/pages/train/SessionsPage.d.ts.map +1 -0
- package/dist/mybodyshield/pages/train/SessionsPage.js +119 -115
- package/dist/mybodyshield/pages/train/SessionsPage.js.map +1 -1
- package/dist/mybodyshield/services/createSandboxAssistantTransport.d.ts +24 -0
- package/dist/mybodyshield/services/createSandboxAssistantTransport.d.ts.map +1 -0
- package/dist/mybodyshield/services/createSandboxAssistantTransport.js +14 -0
- package/dist/mybodyshield/services/createSandboxAssistantTransport.js.map +1 -0
- package/dist/mybodyshield/types/assistant.d.ts +55 -0
- package/dist/mybodyshield/types/assistant.d.ts.map +1 -0
- package/dist/mybodyshield/types/index.d.ts +419 -0
- package/dist/mybodyshield/types/index.d.ts.map +1 -0
- package/dist/mybodyshield/utils/cn.d.ts +4 -0
- package/dist/mybodyshield/utils/cn.d.ts.map +1 -0
- package/dist/mybodyshield/utils/formatters.d.ts +32 -0
- package/dist/mybodyshield/utils/formatters.d.ts.map +1 -0
- package/dist/mybodyshield/utils/i18n.d.ts +6 -0
- package/dist/mybodyshield/utils/i18n.d.ts.map +1 -0
- package/dist/mybodyshield/utils/index.d.ts +9 -0
- package/dist/mybodyshield/utils/index.d.ts.map +1 -0
- package/dist/mybodyshield/utils/navigation.d.ts +14 -0
- package/dist/mybodyshield/utils/navigation.d.ts.map +1 -0
- package/dist/mybodyshield/utils/pageContext.d.ts +28 -0
- package/dist/mybodyshield/utils/pageContext.d.ts.map +1 -0
- package/dist/mybodyshield/utils/pageContext.js +62 -0
- package/dist/mybodyshield/utils/pageContext.js.map +1 -0
- package/package.json +8 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DashboardPage.js","names":[],"sources":["../../../src/mybodyshield/pages/DashboardPage.tsx"],"sourcesContent":["/**\n * Dashboard — MyBodyShield health & fitness overview.\n * @module mybodyshield/pages\n *\n * KPIs are aggregated client-side from the real `myb*` list endpoints:\n * active programs, this-week's sessions, goal progress, today's macros vs\n * target, latest body weight and recovery readiness. No mock data.\n */\n\nimport { useMemo } from 'react';\nimport type { ReactNode } from 'react';\nimport { motion, useReducedMotion } from 'framer-motion';\nimport {\n Dumbbell,\n Flame,\n Target,\n Salad,\n Moon,\n ArrowRight,\n CheckCircle2,\n Weight,\n CalendarCheck,\n} from 'lucide-react';\nimport { GlassCard } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../components/PageLayout';\nimport { MetricsCard } from '../components/MetricsCard';\nimport { StatusBadge } from '../components/StatusBadge';\nimport { MiniBar } from '../components/MiniBar';\nimport { LoadingView, Section } from '../components/StateViews';\nimport { EmptyState } from '../components/EmptyState';\nimport {\n Meter,\n ProgressRing,\n MacroBar,\n MacroLegend,\n IconChip,\n GOAL_TYPE_META,\n RECOVERY_META,\n WORKOUT_TYPE_META,\n computeGoalProgress,\n} from './_shared/visuals';\nimport { useMyBodyShield } from '../context';\nimport {\n useMybPrograms,\n useMybSessions,\n useMybGoals,\n useMybDailyMacros,\n useMybBodyMetrics,\n useMybRecoveryLogs,\n} from '../hooks';\nimport {\n formatNumber,\n formatRelativeTime,\n formatKcal,\n formatWeight,\n formatPercent,\n formatEnumLabel,\n} from '../utils/formatters';\n\n/** Shared easing for entrance / hover motion (smooth ease-out). */\nconst EASE_OUT: [number, number, number, number] = [0.16, 1, 0.3, 1];\n\nexport function DashboardPage() {\n const { navigateTo, currentUser } = useMyBodyShield();\n const reduceMotion = useReducedMotion();\n\n /** Staggered fade/rise entrance — one-shot, skipped under reduced motion. */\n const rise = (i: number) =>\n reduceMotion\n ? {}\n : {\n initial: { opacity: 0, y: 14 },\n whileInView: { opacity: 1, y: 0 },\n viewport: { once: true, amount: 0.15 },\n transition: { duration: 0.45, delay: Math.min(i, 8) * 0.05, ease: EASE_OUT },\n };\n\n /** Gentle hover lift for cards. */\n const lift = reduceMotion\n ? {}\n : { whileHover: { y: -3, transition: { duration: 0.2, ease: EASE_OUT } } };\n\n /** Hover lift + tap feedback for interactive cards. */\n const liftTap = reduceMotion\n ? {}\n : {\n whileHover: { y: -3, transition: { duration: 0.2, ease: EASE_OUT } },\n whileTap: { scale: 0.985 },\n };\n\n const programs = useMybPrograms({ status: 'ACTIVE', pagination: { limit: 50 } });\n const sessions = useMybSessions({ pagination: { limit: 20 } });\n const goals = useMybGoals({ status: 'ACTIVE', pagination: { limit: 50 } });\n const macros = useMybDailyMacros();\n const weightMetrics = useMybBodyMetrics({ type: 'WEIGHT', pagination: { limit: 30 } });\n const readiness = useMybRecoveryLogs('READINESS');\n\n const programRows = programs.data?.items ?? [];\n const sessionRows = sessions.data?.items ?? [];\n const goalRows = goals.data?.items ?? [];\n const weightRows = weightMetrics.data?.items ?? [];\n const readinessRows = readiness.data ?? [];\n\n const now = new Date();\n const weekAgo = now.getTime() - 7 * 24 * 60 * 60 * 1000;\n const sessionsThisWeek = sessionRows.filter((s) => {\n const ref = s.completedAt ?? s.startedAt ?? s.scheduledFor ?? s.createdAt;\n return ref ? new Date(ref).getTime() >= weekAgo : false;\n });\n const completedThisWeek = sessionsThisWeek.filter((s) => s.status === 'COMPLETED').length;\n const activeSession = sessionRows.find((s) => s.status === 'IN_PROGRESS');\n const upcomingSession = sessionRows\n .filter((s) => s.status === 'SCHEDULED' && s.scheduledFor)\n .sort((a, b) => new Date(a.scheduledFor!).getTime() - new Date(b.scheduledFor!).getTime())[0];\n\n const latestWeight = weightRows[0];\n const priorWeight = weightRows[1];\n const weightDelta =\n latestWeight && priorWeight ? latestWeight.value - priorWeight.value : undefined;\n\n const latestReadiness = readinessRows\n .slice()\n .sort((a, b) => new Date(b.loggedAt).getTime() - new Date(a.loggedAt).getTime())[0];\n\n const goalProgressAvg = useMemo(() => {\n if (goalRows.length === 0) return 0;\n const sum = goalRows.reduce((acc, g) => acc + computeGoalProgress(g), 0);\n return sum / goalRows.length;\n }, [goalRows]);\n\n const macroData = macros.data;\n const caloriePct = macroData?.calorieTarget\n ? (macroData.calories / macroData.calorieTarget) * 100\n : 0;\n\n const isInitialLoading =\n programs.isLoading && sessions.isLoading && goals.isLoading && macros.isLoading;\n\n if (isInitialLoading) {\n return (\n <PageLayout title=\"MyBodyShield\" titleGradient aurora>\n <LoadingView label=\"Loading your fitness overview…\" />\n </PageLayout>\n );\n }\n\n const greetingName = currentUser?.firstName || currentUser?.name || 'there';\n\n const kpiCards: { id: string; node: ReactNode }[] = [\n {\n id: 'programs',\n node: (\n <MetricsCard\n icon={<Dumbbell className=\"h-5 w-5\" />}\n value={formatNumber(programRows.length)}\n label=\"Active programs\"\n footer={\n <p className=\"text-xs text-text-tertiary\">\n <button\n type=\"button\"\n onClick={() => navigateTo('programs')}\n className=\"font-medium text-accent hover:underline\"\n >\n Manage programs\n </button>\n </p>\n }\n />\n ),\n },\n {\n id: 'sessions',\n node: (\n <MetricsCard\n icon={<Flame className=\"h-5 w-5\" />}\n value={formatNumber(completedThisWeek)}\n label=\"Sessions this week\"\n accent={activeSession ? 'warning' : undefined}\n footer={\n activeSession ? (\n <button\n type=\"button\"\n onClick={() => navigateTo(`train/${activeSession.id}`)}\n className=\"text-xs font-medium text-status-warning-text hover:underline\"\n >\n Resume live session →\n </button>\n ) : (\n <p className=\"text-xs text-text-tertiary\">\n <span className=\"tabular-nums text-text-secondary\">{sessionsThisWeek.length}</span>{' '}\n logged\n </p>\n )\n }\n />\n ),\n },\n {\n id: 'goals',\n node: (\n <MetricsCard\n icon={<Target className=\"h-5 w-5\" />}\n value={formatNumber(goalRows.length)}\n label=\"Active goals\"\n footer={\n <div className=\"space-y-2\">\n <MiniBar value={goalProgressAvg} height={4} barClassName=\"bg-action-primary-bg\" />\n <p className=\"text-xs text-text-tertiary\">\n <span className=\"tabular-nums text-text-secondary\">\n {formatPercent(goalProgressAvg)}\n </span>{' '}\n avg progress\n </p>\n </div>\n }\n />\n ),\n },\n {\n id: 'weight',\n node: (\n <MetricsCard\n icon={<Weight className=\"h-5 w-5\" />}\n value={latestWeight ? formatWeight(latestWeight.value) : '—'}\n label=\"Latest weight\"\n footer={\n weightDelta != null ? (\n <p className=\"text-xs text-text-tertiary\">\n <span\n className={\n weightDelta <= 0\n ? 'font-medium text-status-success-text'\n : 'font-medium text-text-secondary'\n }\n >\n {weightDelta > 0 ? '+' : ''}\n {weightDelta.toFixed(1)} kg\n </span>{' '}\n vs last\n </p>\n ) : (\n <button\n type=\"button\"\n onClick={() => navigateTo('progress')}\n className=\"text-xs font-medium text-accent hover:underline\"\n >\n Log a measurement →\n </button>\n )\n }\n />\n ),\n },\n {\n id: 'recovery',\n node: (\n <MetricsCard\n icon={<Moon className=\"h-5 w-5\" />}\n value={latestReadiness ? `${latestReadiness.score}` : '—'}\n label=\"Recovery readiness\"\n accent={\n latestReadiness\n ? latestReadiness.score >= 70\n ? 'success'\n : latestReadiness.score >= 40\n ? 'warning'\n : 'error'\n : undefined\n }\n footer={\n latestReadiness ? (\n <p className=\"text-xs text-text-tertiary\">\n {formatRelativeTime(latestReadiness.loggedAt)}\n </p>\n ) : (\n <button\n type=\"button\"\n onClick={() => navigateTo('recovery')}\n className=\"text-xs font-medium text-accent hover:underline\"\n >\n Log recovery →\n </button>\n )\n }\n />\n ),\n },\n ];\n\n return (\n <PageLayout\n title=\"MyBodyShield\"\n titleGradient\n aurora\n description={`Welcome back, ${greetingName} — your training, nutrition, progress & recovery control room.`}\n >\n {/* KPI grid */}\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-5\">\n {kpiCards.map((card, i) => (\n <motion.div\n key={card.id}\n className=\"group relative rounded-2xl transition-shadow duration-300 hover:shadow-float\"\n {...rise(i)}\n {...lift}\n >\n {/* Emerald top-edge accent — brightens on hover */}\n <span\n aria-hidden\n className=\"pointer-events-none absolute inset-x-6 top-0 z-10 h-px rounded-full bg-gradient-to-r from-transparent via-action-primary-bg to-transparent opacity-40 transition-opacity duration-300 group-hover:opacity-90\"\n />\n {card.node}\n </motion.div>\n ))}\n </div>\n\n <div className=\"mt-6 grid grid-cols-1 gap-6 lg:grid-cols-3\">\n {/* Today's nutrition */}\n <motion.div className=\"lg:col-span-2\" {...rise(0)}>\n <GlassCard treatment=\"glass\" className=\"h-full p-6\">\n <div className=\"mb-4 flex flex-wrap items-start justify-between gap-3\">\n <div>\n <h2 className=\"text-base font-semibold text-text-primary\">\n Today's nutrition\n </h2>\n <p className=\"mt-0.5 text-sm text-text-secondary\">\n Calories & macros logged against your active plan.\n </p>\n </div>\n <button\n type=\"button\"\n onClick={() => navigateTo('nutrition')}\n className=\"inline-flex items-center gap-1 text-sm font-medium text-accent hover:underline\"\n >\n Nutrition <ArrowRight className=\"h-4 w-4\" />\n </button>\n </div>\n {!macroData || (macroData.calorieTarget === 0 && macroData.calories === 0) ? (\n <EmptyState\n illustration=\"empty-data\"\n icon={<Salad className=\"h-10 w-10\" />}\n title=\"No nutrition data yet\"\n description=\"Activate a nutrition plan and log meals to track your daily macros here.\"\n />\n ) : (\n <div className=\"flex flex-col gap-8 sm:flex-row sm:items-center\">\n {/* Calorie ring — the nutrition hero */}\n <div className=\"relative mx-auto flex shrink-0 items-center justify-center sm:mx-0\">\n <span\n aria-hidden\n className=\"pointer-events-none absolute inset-2 rounded-full opacity-70 blur-2xl\"\n style={{\n background:\n 'radial-gradient(circle, color-mix(in srgb, var(--color-action-primary-bg) 26%, transparent), transparent 70%)',\n }}\n />\n <ProgressRing value={caloriePct} size={188} stroke={16}>\n <span className=\"text-[11px] font-medium uppercase tracking-wide text-text-tertiary\">\n Calories\n </span>\n <span className=\"text-3xl font-bold tabular-nums text-text-primary\">\n {formatNumber(macroData.calories)}\n </span>\n <span className=\"mt-0.5 text-xs text-text-tertiary\">\n / {formatNumber(macroData.calorieTarget)} kcal\n </span>\n </ProgressRing>\n </div>\n <div className=\"flex-1 space-y-4\">\n <div className=\"space-y-2\">\n <MacroBar\n proteinG={macroData.proteinG}\n carbsG={macroData.carbsG}\n fatG={macroData.fatG}\n height={12}\n />\n <MacroLegend />\n </div>\n <div className=\"grid grid-cols-3 gap-3\">\n {(\n [\n ['Protein', macroData.proteinG, macroData.proteinTarget],\n ['Carbs', macroData.carbsG, macroData.carbsTarget],\n ['Fat', macroData.fatG, macroData.fatTarget],\n ] as const\n ).map(([label, val, target]) => {\n const pct = target ? (val / target) * 100 : 0;\n return (\n <div\n key={label}\n className=\"rounded-2xl bg-bg-sunken/60 p-3 ring-1 ring-inset ring-border-seam/40 transition-shadow hover:shadow-float\"\n >\n <div className=\"flex items-center justify-between gap-1\">\n <p className=\"text-xs font-medium text-text-tertiary\">{label}</p>\n <span className=\"text-[11px] tabular-nums text-text-tertiary\">\n {formatPercent(pct)}\n </span>\n </div>\n <p className=\"mt-1 text-sm font-semibold tabular-nums text-text-primary\">\n {formatNumber(val)}\n <span className=\"text-xs font-normal text-text-tertiary\">\n {' '}\n / {formatNumber(target)} g\n </span>\n </p>\n <Meter value={pct} height={5} className=\"mt-2\" />\n </div>\n );\n })}\n </div>\n </div>\n </div>\n )}\n </GlassCard>\n </motion.div>\n\n {/* Next up */}\n <motion.div {...rise(1)}>\n <GlassCard treatment=\"glass\" className=\"h-full p-6\">\n <div className=\"mb-4 flex items-center justify-between gap-3\">\n <h2 className=\"text-base font-semibold text-text-primary\">Next up</h2>\n <button\n type=\"button\"\n onClick={() => navigateTo('train')}\n className=\"inline-flex items-center gap-1 text-sm font-medium text-accent hover:underline\"\n >\n Train <ArrowRight className=\"h-4 w-4\" />\n </button>\n </div>\n {activeSession ? (\n <motion.button\n type=\"button\"\n onClick={() => navigateTo(`train/${activeSession.id}`)}\n className=\"w-full rounded-2xl border border-status-warning-text/40 bg-status-warning-bg-subtle p-4 text-left transition-shadow hover:shadow-float\"\n {...liftTap}\n >\n <div className=\"flex items-center gap-2\">\n <StatusBadge status={activeSession.status} />\n </div>\n <p className=\"mt-2 text-sm font-medium text-text-primary\">\n Live workout in progress\n </p>\n <p className=\"mt-0.5 text-xs text-text-tertiary\">\n Started {formatRelativeTime(activeSession.startedAt)}\n </p>\n </motion.button>\n ) : upcomingSession ? (\n <motion.button\n type=\"button\"\n onClick={() => navigateTo(`train/${upcomingSession.id}`)}\n className=\"w-full rounded-2xl bg-bg-sunken/60 p-4 text-left ring-1 ring-inset ring-border-seam/40 transition-shadow hover:shadow-float\"\n {...liftTap}\n >\n <div className=\"flex items-center gap-2\">\n <IconChip icon={<CalendarCheck className=\"h-4 w-4\" />} />\n <div>\n <p className=\"text-sm font-medium text-text-primary\">Scheduled session</p>\n <p className=\"text-xs text-text-tertiary\">\n {formatRelativeTime(upcomingSession.scheduledFor)}\n </p>\n </div>\n </div>\n </motion.button>\n ) : (\n <div className=\"flex flex-col items-center justify-center rounded-2xl border border-dashed border-border-seam py-8 text-center\">\n <span className=\"flex size-11 items-center justify-center rounded-full bg-[var(--color-accent-soft)]\">\n <Flame className=\"h-5 w-5 text-accent\" />\n </span>\n <p className=\"mt-3 text-sm font-medium text-text-primary\">No session scheduled</p>\n <button\n type=\"button\"\n onClick={() => navigateTo('train')}\n className=\"mt-1 text-xs font-medium text-accent hover:underline\"\n >\n Start a workout\n </button>\n </div>\n )}\n </GlassCard>\n </motion.div>\n </div>\n\n {/* Active goals */}\n <Section\n title=\"Active goals\"\n description=\"Your current targets across training, nutrition and body composition.\"\n action={\n <button\n type=\"button\"\n onClick={() => navigateTo('goals')}\n className=\"inline-flex items-center gap-1 text-sm font-medium text-accent hover:underline\"\n >\n All goals <ArrowRight className=\"h-4 w-4\" />\n </button>\n }\n >\n {goalRows.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n icon={<Target className=\"h-10 w-10\" />}\n title=\"No active goals\"\n description=\"Set a goal to give your training and nutrition a clear target to work toward.\"\n />\n ) : (\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n {goalRows.slice(0, 6).map((g, i) => {\n const pct = computeGoalProgress(g);\n const meta = GOAL_TYPE_META[g.type];\n const Icon = meta.icon;\n return (\n <motion.button\n key={g.id}\n type=\"button\"\n onClick={() => navigateTo('goals')}\n className=\"text-left\"\n {...rise(i)}\n {...liftTap}\n >\n <GlassCard\n treatment=\"plain\"\n className=\"h-full p-4 transition-shadow hover:shadow-float\"\n >\n <div className=\"flex items-start gap-3\">\n <IconChip icon={<Icon className=\"h-4 w-4\" />} tint={meta.tint} />\n <div className=\"min-w-0 flex-1\">\n <p className=\"truncate text-sm font-semibold text-text-primary\">\n {g.title}\n </p>\n <p className=\"text-xs text-text-tertiary\">{formatEnumLabel(g.type)}</p>\n </div>\n </div>\n <div className=\"mt-3 space-y-1.5\">\n <Meter value={pct} />\n <div className=\"flex items-center justify-between text-xs\">\n <span className=\"tabular-nums text-text-secondary\">\n {formatNumber(g.currentValue)}\n {g.unit ? ` ${g.unit}` : ''} / {formatNumber(g.targetValue)}\n {g.unit ? ` ${g.unit}` : ''}\n </span>\n <span className=\"tabular-nums text-text-tertiary\">\n {formatPercent(pct)}\n </span>\n </div>\n </div>\n </GlassCard>\n </motion.button>\n );\n })}\n </div>\n )}\n </Section>\n\n {/* Recent sessions */}\n <Section\n title=\"Recent sessions\"\n description=\"Your latest logged and scheduled workouts.\"\n action={\n <button\n type=\"button\"\n onClick={() => navigateTo('train')}\n className=\"inline-flex items-center gap-1 text-sm font-medium text-accent hover:underline\"\n >\n All sessions <ArrowRight className=\"h-4 w-4\" />\n </button>\n }\n >\n {sessionRows.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n icon={<Flame className=\"h-10 w-10\" />}\n title=\"No sessions yet\"\n description=\"Start a workout session to begin logging sets and tracking your training.\"\n />\n ) : (\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n {sessionRows.slice(0, 6).map((s, i) => {\n const meta = WORKOUT_TYPE_META.STRENGTH;\n const Icon = s.status === 'COMPLETED' ? CheckCircle2 : meta.icon;\n return (\n <motion.button\n key={s.id}\n type=\"button\"\n onClick={() => navigateTo(`train/${s.id}`)}\n className=\"text-left\"\n {...rise(i)}\n {...liftTap}\n >\n <GlassCard treatment=\"plain\" className=\"p-4 transition-shadow hover:shadow-float\">\n <div className=\"flex items-center justify-between gap-2\">\n <IconChip icon={<Icon className=\"h-4 w-4\" />} tint={meta.tint} />\n <StatusBadge status={s.status} />\n </div>\n <p className=\"mt-3 text-sm font-medium text-text-primary\">\n {s.caloriesBurned > 0\n ? `${formatNumber(s.caloriesBurned)} kcal burned`\n : 'Workout session'}\n </p>\n <p className=\"mt-0.5 text-xs text-text-tertiary\">\n {formatRelativeTime(s.completedAt ?? s.startedAt ?? s.createdAt)}\n </p>\n </GlassCard>\n </motion.button>\n );\n })}\n </div>\n )}\n </Section>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"DashboardPage.js","names":[],"sources":["../../../src/mybodyshield/pages/DashboardPage.tsx"],"sourcesContent":["/**\n * Dashboard — MyBodyShield health & fitness overview.\n * @module mybodyshield/pages\n *\n * KPIs are aggregated client-side from the real `myb*` list endpoints:\n * active programs, this-week's sessions, goal progress, today's macros vs\n * target, latest body weight and recovery readiness. No mock data.\n */\n\nimport { useMemo } from 'react';\nimport type { ReactNode } from 'react';\nimport { motion, useReducedMotion } from 'framer-motion';\nimport {\n Dumbbell,\n Flame,\n Target,\n Salad,\n Moon,\n ArrowRight,\n CheckCircle2,\n Weight,\n CalendarCheck,\n} from 'lucide-react';\nimport { GlassCard } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../components/PageLayout';\nimport { MetricsCard } from '../components/MetricsCard';\nimport { StatusBadge } from '../components/StatusBadge';\nimport { MiniBar } from '../components/MiniBar';\nimport { LoadingView, Section } from '../components/StateViews';\nimport { EmptyState } from '../components/EmptyState';\nimport {\n Meter,\n ProgressRing,\n MacroBar,\n MacroLegend,\n IconChip,\n GOAL_TYPE_META,\n RECOVERY_META,\n WORKOUT_TYPE_META,\n computeGoalProgress,\n} from './_shared/visuals';\nimport { useMyBodyShield } from '../context';\nimport {\n useMybPrograms,\n useMybSessions,\n useMybGoals,\n useMybDailyMacros,\n useMybBodyMetrics,\n useMybRecoveryLogs,\n} from '../hooks';\n\nimport {\n formatNumber,\n formatRelativeTime,\n formatKcal,\n formatWeight,\n formatPercent,\n formatEnumLabel,\n} from '../utils/formatters';\n\n/** Shared easing for entrance / hover motion (smooth ease-out). */\nconst EASE_OUT: [number, number, number, number] = [0.16, 1, 0.3, 1];\n\nexport function DashboardPage() {\n const { navigateTo, currentUser } = useMyBodyShield();\n const reduceMotion = useReducedMotion();\n\n /** Staggered fade/rise entrance — one-shot, skipped under reduced motion. */\n const rise = (i: number) =>\n reduceMotion\n ? {}\n : {\n initial: { opacity: 0, y: 14 },\n whileInView: { opacity: 1, y: 0 },\n viewport: { once: true, amount: 0.15 },\n transition: { duration: 0.45, delay: Math.min(i, 8) * 0.05, ease: EASE_OUT },\n };\n\n /** Gentle hover lift for cards. */\n const lift = reduceMotion\n ? {}\n : { whileHover: { y: -3, transition: { duration: 0.2, ease: EASE_OUT } } };\n\n /** Hover lift + tap feedback for interactive cards. */\n const liftTap = reduceMotion\n ? {}\n : {\n whileHover: { y: -3, transition: { duration: 0.2, ease: EASE_OUT } },\n whileTap: { scale: 0.985 },\n };\n\n const programs = useMybPrograms({ status: 'ACTIVE', pagination: { limit: 50 } });\n const sessions = useMybSessions({ pagination: { limit: 20 } });\n const goals = useMybGoals({ status: 'ACTIVE', pagination: { limit: 50 } });\n const macros = useMybDailyMacros();\n const weightMetrics = useMybBodyMetrics({ type: 'WEIGHT', pagination: { limit: 30 } });\n const readiness = useMybRecoveryLogs('READINESS');\n\n const programRows = programs.data?.items ?? [];\n const sessionRows = sessions.data?.items ?? [];\n const goalRows = goals.data?.items ?? [];\n const weightRows = weightMetrics.data?.items ?? [];\n const readinessRows = readiness.data ?? [];\n\n const now = new Date();\n const weekAgo = now.getTime() - 7 * 24 * 60 * 60 * 1000;\n const sessionsThisWeek = sessionRows.filter((s) => {\n const ref = s.completedAt ?? s.startedAt ?? s.scheduledFor ?? s.createdAt;\n return ref ? new Date(ref).getTime() >= weekAgo : false;\n });\n const completedThisWeek = sessionsThisWeek.filter((s) => s.status === 'COMPLETED').length;\n const activeSession = sessionRows.find((s) => s.status === 'IN_PROGRESS');\n const upcomingSession = sessionRows\n .filter((s) => s.status === 'SCHEDULED' && s.scheduledFor)\n .sort((a, b) => new Date(a.scheduledFor!).getTime() - new Date(b.scheduledFor!).getTime())[0];\n\n const latestWeight = weightRows[0];\n const priorWeight = weightRows[1];\n const weightDelta =\n latestWeight && priorWeight ? latestWeight.value - priorWeight.value : undefined;\n\n const latestReadiness = readinessRows\n .slice()\n .sort((a, b) => new Date(b.loggedAt).getTime() - new Date(a.loggedAt).getTime())[0];\n\n const goalProgressAvg = useMemo(() => {\n if (goalRows.length === 0) return 0;\n const sum = goalRows.reduce((acc, g) => acc + computeGoalProgress(g), 0);\n return sum / goalRows.length;\n }, [goalRows]);\n\n const macroData = macros.data;\n const caloriePct = macroData?.calorieTarget\n ? (macroData.calories / macroData.calorieTarget) * 100\n : 0;\n\n const isInitialLoading =\n programs.isLoading && sessions.isLoading && goals.isLoading && macros.isLoading;\n\n if (isInitialLoading) {\n return (\n <PageLayout title=\"MyBodyShield\" titleGradient aurora>\n <LoadingView label=\"Loading your fitness overview…\" />\n </PageLayout>\n );\n }\n\n const greetingName = currentUser?.firstName || currentUser?.name || 'there';\n\n const kpiCards: { id: string; node: ReactNode }[] = [\n {\n id: 'programs',\n node: (\n <MetricsCard\n icon={<Dumbbell className=\"h-5 w-5\" />}\n value={formatNumber(programRows.length)}\n label=\"Active programs\"\n footer={\n <p className=\"text-xs text-text-tertiary\">\n <button\n type=\"button\"\n onClick={() => navigateTo('programs')}\n className=\"font-medium text-accent hover:underline\"\n >\n Manage programs\n </button>\n </p>\n }\n />\n ),\n },\n {\n id: 'sessions',\n node: (\n <MetricsCard\n icon={<Flame className=\"h-5 w-5\" />}\n value={formatNumber(completedThisWeek)}\n label=\"Sessions this week\"\n accent={activeSession ? 'warning' : undefined}\n footer={\n activeSession ? (\n <button\n type=\"button\"\n onClick={() => navigateTo(`train/${activeSession.id}`)}\n className=\"text-xs font-medium text-status-warning-text hover:underline\"\n >\n Resume live session →\n </button>\n ) : (\n <p className=\"text-xs text-text-tertiary\">\n <span className=\"tabular-nums text-text-secondary\">{sessionsThisWeek.length}</span>{' '}\n logged\n </p>\n )\n }\n />\n ),\n },\n {\n id: 'goals',\n node: (\n <MetricsCard\n icon={<Target className=\"h-5 w-5\" />}\n value={formatNumber(goalRows.length)}\n label=\"Active goals\"\n footer={\n <div className=\"space-y-2\">\n <MiniBar value={goalProgressAvg} height={4} barClassName=\"bg-action-primary-bg\" />\n <p className=\"text-xs text-text-tertiary\">\n <span className=\"tabular-nums text-text-secondary\">\n {formatPercent(goalProgressAvg)}\n </span>{' '}\n avg progress\n </p>\n </div>\n }\n />\n ),\n },\n {\n id: 'weight',\n node: (\n <MetricsCard\n icon={<Weight className=\"h-5 w-5\" />}\n value={latestWeight ? formatWeight(latestWeight.value) : '—'}\n label=\"Latest weight\"\n footer={\n weightDelta != null ? (\n <p className=\"text-xs text-text-tertiary\">\n <span\n className={\n weightDelta <= 0\n ? 'font-medium text-status-success-text'\n : 'font-medium text-text-secondary'\n }\n >\n {weightDelta > 0 ? '+' : ''}\n {weightDelta.toFixed(1)} kg\n </span>{' '}\n vs last\n </p>\n ) : (\n <button\n type=\"button\"\n onClick={() => navigateTo('progress')}\n className=\"text-xs font-medium text-accent hover:underline\"\n >\n Log a measurement →\n </button>\n )\n }\n />\n ),\n },\n {\n id: 'recovery',\n node: (\n <MetricsCard\n icon={<Moon className=\"h-5 w-5\" />}\n value={latestReadiness ? `${latestReadiness.score}` : '—'}\n label=\"Recovery readiness\"\n accent={\n latestReadiness\n ? latestReadiness.score >= 70\n ? 'success'\n : latestReadiness.score >= 40\n ? 'warning'\n : 'error'\n : undefined\n }\n footer={\n latestReadiness ? (\n <p className=\"text-xs text-text-tertiary\">\n {formatRelativeTime(latestReadiness.loggedAt)}\n </p>\n ) : (\n <button\n type=\"button\"\n onClick={() => navigateTo('recovery')}\n className=\"text-xs font-medium text-accent hover:underline\"\n >\n Log recovery →\n </button>\n )\n }\n />\n ),\n },\n ];\n\n return (\n <PageLayout\n title=\"MyBodyShield\"\n titleGradient\n aurora\n description={`Welcome back, ${greetingName} — your training, nutrition, progress & recovery control room.`}\n >\n {/* KPI grid */}\n <div\n data-tour=\"mybodyshield-dashboard-kpi-grid\"\n className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-5\"\n >\n {kpiCards.map((card, i) => (\n <motion.div\n key={card.id}\n className=\"group relative rounded-2xl transition-shadow duration-300 hover:shadow-float\"\n {...rise(i)}\n {...lift}\n >\n {/* Emerald top-edge accent — brightens on hover */}\n <span\n aria-hidden\n className=\"pointer-events-none absolute inset-x-6 top-0 z-10 h-px rounded-full bg-gradient-to-r from-transparent via-action-primary-bg to-transparent opacity-40 transition-opacity duration-300 group-hover:opacity-90\"\n />\n {card.node}\n </motion.div>\n ))}\n </div>\n\n <div className=\"mt-6 grid grid-cols-1 gap-6 lg:grid-cols-3\">\n {/* Today's nutrition */}\n <motion.div\n data-tour=\"mybodyshield-dashboard-nutrition\"\n className=\"lg:col-span-2\"\n {...rise(0)}\n >\n <GlassCard treatment=\"glass\" className=\"h-full p-6\">\n <div className=\"mb-4 flex flex-wrap items-start justify-between gap-3\">\n <div>\n <h2 className=\"text-base font-semibold text-text-primary\">\n Today's nutrition\n </h2>\n <p className=\"mt-0.5 text-sm text-text-secondary\">\n Calories & macros logged against your active plan.\n </p>\n </div>\n <button\n type=\"button\"\n onClick={() => navigateTo('nutrition')}\n className=\"inline-flex items-center gap-1 text-sm font-medium text-accent hover:underline\"\n >\n Nutrition <ArrowRight className=\"h-4 w-4\" />\n </button>\n </div>\n {!macroData || (macroData.calorieTarget === 0 && macroData.calories === 0) ? (\n <EmptyState\n illustration=\"empty-data\"\n icon={<Salad className=\"h-10 w-10\" />}\n title=\"No nutrition data yet\"\n description=\"Activate a nutrition plan and log meals to track your daily macros here.\"\n />\n ) : (\n <div className=\"flex flex-col gap-8 sm:flex-row sm:items-center\">\n {/* Calorie ring — the nutrition hero */}\n <div className=\"relative mx-auto flex shrink-0 items-center justify-center sm:mx-0\">\n <span\n aria-hidden\n className=\"pointer-events-none absolute inset-2 rounded-full opacity-70 blur-2xl\"\n style={{\n background:\n 'radial-gradient(circle, color-mix(in srgb, var(--color-action-primary-bg) 26%, transparent), transparent 70%)',\n }}\n />\n <ProgressRing value={caloriePct} size={188} stroke={16}>\n <span className=\"text-[11px] font-medium uppercase tracking-wide text-text-tertiary\">\n Calories\n </span>\n <span className=\"text-3xl font-bold tabular-nums text-text-primary\">\n {formatNumber(macroData.calories)}\n </span>\n <span className=\"mt-0.5 text-xs text-text-tertiary\">\n / {formatNumber(macroData.calorieTarget)} kcal\n </span>\n </ProgressRing>\n </div>\n <div className=\"flex-1 space-y-4\">\n <div className=\"space-y-2\">\n <MacroBar\n proteinG={macroData.proteinG}\n carbsG={macroData.carbsG}\n fatG={macroData.fatG}\n height={12}\n />\n <MacroLegend />\n </div>\n <div className=\"grid grid-cols-3 gap-3\">\n {(\n [\n ['Protein', macroData.proteinG, macroData.proteinTarget],\n ['Carbs', macroData.carbsG, macroData.carbsTarget],\n ['Fat', macroData.fatG, macroData.fatTarget],\n ] as const\n ).map(([label, val, target]) => {\n const pct = target ? (val / target) * 100 : 0;\n return (\n <div\n key={label}\n className=\"rounded-2xl bg-bg-sunken/60 p-3 ring-1 ring-inset ring-border-seam/40 transition-shadow hover:shadow-float\"\n >\n <div className=\"flex items-center justify-between gap-1\">\n <p className=\"text-xs font-medium text-text-tertiary\">{label}</p>\n <span className=\"text-[11px] tabular-nums text-text-tertiary\">\n {formatPercent(pct)}\n </span>\n </div>\n <p className=\"mt-1 text-sm font-semibold tabular-nums text-text-primary\">\n {formatNumber(val)}\n <span className=\"text-xs font-normal text-text-tertiary\">\n {' '}\n / {formatNumber(target)} g\n </span>\n </p>\n <Meter value={pct} height={5} className=\"mt-2\" />\n </div>\n );\n })}\n </div>\n </div>\n </div>\n )}\n </GlassCard>\n </motion.div>\n\n {/* Next up */}\n <motion.div data-tour=\"mybodyshield-dashboard-next-up\" {...rise(1)}>\n <GlassCard treatment=\"glass\" className=\"h-full p-6\">\n <div className=\"mb-4 flex items-center justify-between gap-3\">\n <h2 className=\"text-base font-semibold text-text-primary\">Next up</h2>\n <button\n type=\"button\"\n onClick={() => navigateTo('train')}\n className=\"inline-flex items-center gap-1 text-sm font-medium text-accent hover:underline\"\n >\n Train <ArrowRight className=\"h-4 w-4\" />\n </button>\n </div>\n {activeSession ? (\n <motion.button\n type=\"button\"\n onClick={() => navigateTo(`train/${activeSession.id}`)}\n className=\"w-full rounded-2xl border border-status-warning-text/40 bg-status-warning-bg-subtle p-4 text-left transition-shadow hover:shadow-float\"\n {...liftTap}\n >\n <div className=\"flex items-center gap-2\">\n <StatusBadge status={activeSession.status} />\n </div>\n <p className=\"mt-2 text-sm font-medium text-text-primary\">\n Live workout in progress\n </p>\n <p className=\"mt-0.5 text-xs text-text-tertiary\">\n Started {formatRelativeTime(activeSession.startedAt)}\n </p>\n </motion.button>\n ) : upcomingSession ? (\n <motion.button\n type=\"button\"\n onClick={() => navigateTo(`train/${upcomingSession.id}`)}\n className=\"w-full rounded-2xl bg-bg-sunken/60 p-4 text-left ring-1 ring-inset ring-border-seam/40 transition-shadow hover:shadow-float\"\n {...liftTap}\n >\n <div className=\"flex items-center gap-2\">\n <IconChip icon={<CalendarCheck className=\"h-4 w-4\" />} />\n <div>\n <p className=\"text-sm font-medium text-text-primary\">Scheduled session</p>\n <p className=\"text-xs text-text-tertiary\">\n {formatRelativeTime(upcomingSession.scheduledFor)}\n </p>\n </div>\n </div>\n </motion.button>\n ) : (\n <div className=\"flex flex-col items-center justify-center rounded-2xl border border-dashed border-border-seam py-8 text-center\">\n <span className=\"flex size-11 items-center justify-center rounded-full bg-[var(--color-accent-soft)]\">\n <Flame className=\"h-5 w-5 text-accent\" />\n </span>\n <p className=\"mt-3 text-sm font-medium text-text-primary\">No session scheduled</p>\n <button\n type=\"button\"\n onClick={() => navigateTo('train')}\n className=\"mt-1 text-xs font-medium text-accent hover:underline\"\n >\n Start a workout\n </button>\n </div>\n )}\n </GlassCard>\n </motion.div>\n </div>\n\n {/* Active goals */}\n <Section\n data-tour=\"mybodyshield-dashboard-active-goals\"\n title=\"Active goals\"\n description=\"Your current targets across training, nutrition and body composition.\"\n action={\n <button\n type=\"button\"\n onClick={() => navigateTo('goals')}\n className=\"inline-flex items-center gap-1 text-sm font-medium text-accent hover:underline\"\n >\n All goals <ArrowRight className=\"h-4 w-4\" />\n </button>\n }\n >\n {goalRows.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n icon={<Target className=\"h-10 w-10\" />}\n title=\"No active goals\"\n description=\"Set a goal to give your training and nutrition a clear target to work toward.\"\n />\n ) : (\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n {goalRows.slice(0, 6).map((g, i) => {\n const pct = computeGoalProgress(g);\n const meta = GOAL_TYPE_META[g.type];\n const Icon = meta.icon;\n return (\n <motion.button\n key={g.id}\n type=\"button\"\n onClick={() => navigateTo('goals')}\n className=\"text-left\"\n {...rise(i)}\n {...liftTap}\n >\n <GlassCard\n treatment=\"plain\"\n className=\"h-full p-4 transition-shadow hover:shadow-float\"\n >\n <div className=\"flex items-start gap-3\">\n <IconChip icon={<Icon className=\"h-4 w-4\" />} tint={meta.tint} />\n <div className=\"min-w-0 flex-1\">\n <p className=\"truncate text-sm font-semibold text-text-primary\">\n {g.title}\n </p>\n <p className=\"text-xs text-text-tertiary\">{formatEnumLabel(g.type)}</p>\n </div>\n </div>\n <div className=\"mt-3 space-y-1.5\">\n <Meter value={pct} />\n <div className=\"flex items-center justify-between text-xs\">\n <span className=\"tabular-nums text-text-secondary\">\n {formatNumber(g.currentValue)}\n {g.unit ? ` ${g.unit}` : ''} / {formatNumber(g.targetValue)}\n {g.unit ? ` ${g.unit}` : ''}\n </span>\n <span className=\"tabular-nums text-text-tertiary\">\n {formatPercent(pct)}\n </span>\n </div>\n </div>\n </GlassCard>\n </motion.button>\n );\n })}\n </div>\n )}\n </Section>\n\n {/* Recent sessions */}\n <Section\n data-tour=\"mybodyshield-dashboard-recent-sessions\"\n title=\"Recent sessions\"\n description=\"Your latest logged and scheduled workouts.\"\n action={\n <button\n type=\"button\"\n onClick={() => navigateTo('train')}\n className=\"inline-flex items-center gap-1 text-sm font-medium text-accent hover:underline\"\n >\n All sessions <ArrowRight className=\"h-4 w-4\" />\n </button>\n }\n >\n {sessionRows.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n icon={<Flame className=\"h-10 w-10\" />}\n title=\"No sessions yet\"\n description=\"Start a workout session to begin logging sets and tracking your training.\"\n />\n ) : (\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n {sessionRows.slice(0, 6).map((s, i) => {\n const meta = WORKOUT_TYPE_META.STRENGTH;\n const Icon = s.status === 'COMPLETED' ? CheckCircle2 : meta.icon;\n return (\n <motion.button\n key={s.id}\n type=\"button\"\n onClick={() => navigateTo(`train/${s.id}`)}\n className=\"text-left\"\n {...rise(i)}\n {...liftTap}\n >\n <GlassCard treatment=\"plain\" className=\"p-4 transition-shadow hover:shadow-float\">\n <div className=\"flex items-center justify-between gap-2\">\n <IconChip icon={<Icon className=\"h-4 w-4\" />} tint={meta.tint} />\n <StatusBadge status={s.status} />\n </div>\n <p className=\"mt-3 text-sm font-medium text-text-primary\">\n {s.caloriesBurned > 0\n ? `${formatNumber(s.caloriesBurned)} kcal burned`\n : 'Workout session'}\n </p>\n <p className=\"mt-0.5 text-xs text-text-tertiary\">\n {formatRelativeTime(s.completedAt ?? s.startedAt ?? s.createdAt)}\n </p>\n </GlassCard>\n </motion.button>\n );\n })}\n </div>\n )}\n </Section>\n </PageLayout>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AA6DA,IAAM,IAA6C;CAAC;CAAM;CAAG;CAAK;CAAE;AAEpE,SAAgB,IAAgB;CAC9B,IAAM,EAAE,eAAY,mBAAgB,GAAiB,EAC/C,IAAe,GAAkB,EAGjC,KAAQ,MACZ,IACI,EAAE,GACF;EACE,SAAS;GAAE,SAAS;GAAG,GAAG;GAAI;EAC9B,aAAa;GAAE,SAAS;GAAG,GAAG;GAAG;EACjC,UAAU;GAAE,MAAM;GAAM,QAAQ;GAAM;EACtC,YAAY;GAAE,UAAU;GAAM,OAAO,KAAK,IAAI,GAAG,EAAE,GAAG;GAAM,MAAM;GAAU;EAC7E,EAGD,IAAO,IACT,EAAE,GACF,EAAE,YAAY;EAAE,GAAG;EAAI,YAAY;GAAE,UAAU;GAAK,MAAM;GAAU;EAAE,EAAE,EAGtE,IAAU,IACZ,EAAE,GACF;EACE,YAAY;GAAE,GAAG;GAAI,YAAY;IAAE,UAAU;IAAK,MAAM;IAAU;GAAE;EACpE,UAAU,EAAE,OAAO,MAAO;EAC3B,EAEC,IAAW,GAAe;EAAE,QAAQ;EAAU,YAAY,EAAE,OAAO,IAAI;EAAE,CAAC,EAC1E,IAAW,GAAe,EAAE,YAAY,EAAE,OAAO,IAAI,EAAE,CAAC,EACxD,IAAQ,GAAY;EAAE,QAAQ;EAAU,YAAY,EAAE,OAAO,IAAI;EAAE,CAAC,EACpE,IAAS,IAAmB,EAC5B,IAAgB,EAAkB;EAAE,MAAM;EAAU,YAAY,EAAE,OAAO,IAAI;EAAE,CAAC,EAChF,KAAY,GAAmB,YAAY,EAE3C,KAAc,EAAS,MAAM,SAAS,EAAE,EACxC,IAAc,EAAS,MAAM,SAAS,EAAE,EACxC,IAAW,EAAM,MAAM,SAAS,EAAE,EAClC,IAAa,EAAc,MAAM,SAAS,EAAE,EAC5C,KAAgB,GAAU,QAAQ,EAAE,EAGpC,sBADM,IAAI,MAAM,EACF,SAAS,GAAG,QAAc,KAAK,KAC7C,IAAmB,EAAY,QAAQ,MAAM;EACjD,IAAM,IAAM,EAAE,eAAe,EAAE,aAAa,EAAE,gBAAgB,EAAE;AAChE,SAAO,IAAM,IAAI,KAAK,EAAI,CAAC,SAAS,IAAI,KAAU;GAClD,EACI,KAAoB,EAAiB,QAAQ,MAAM,EAAE,WAAW,YAAY,CAAC,QAC7E,IAAgB,EAAY,MAAM,MAAM,EAAE,WAAW,cAAc,EACnE,IAAkB,EACrB,QAAQ,MAAM,EAAE,WAAW,eAAe,EAAE,aAAa,CACzD,MAAM,GAAG,MAAM,IAAI,KAAK,EAAE,aAAc,CAAC,SAAS,GAAG,IAAI,KAAK,EAAE,aAAc,CAAC,SAAS,CAAC,CAAC,IAEvF,IAAe,EAAW,IAC1B,IAAc,EAAW,IACzB,IACJ,KAAgB,IAAc,EAAa,QAAQ,EAAY,QAAQ,KAAA,GAEnE,IAAkB,GACrB,OAAO,CACP,MAAM,GAAG,MAAM,IAAI,KAAK,EAAE,SAAS,CAAC,SAAS,GAAG,IAAI,KAAK,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC,IAE7E,IAAkB,QAClB,EAAS,WAAW,IAAU,IACtB,EAAS,QAAQ,GAAK,MAAM,IAAM,EAAoB,EAAE,EAAE,EAAE,GAC3D,EAAS,QACrB,CAAC,EAAS,CAAC,EAER,IAAY,EAAO,MACnB,KAAa,GAAW,gBACzB,EAAU,WAAW,EAAU,gBAAiB,MACjD;AAKJ,KAFE,EAAS,aAAa,EAAS,aAAa,EAAM,aAAa,EAAO,UAGtE,QACE,kBAAC,GAAD;EAAY,OAAM;EAAe,eAAA;EAAc,QAAA;YAC7C,kBAAC,GAAD,EAAa,OAAM,kCAAmC,CAAA;EAC3C,CAAA;CAIjB,IAAM,KAAe,GAAa,aAAa,GAAa,QAAQ,SAE9D,KAA8C;EAClD;GACE,IAAI;GACJ,MACE,kBAAC,GAAD;IACE,MAAM,kBAAC,GAAD,EAAU,WAAU,WAAY,CAAA;IACtC,OAAO,EAAa,GAAY,OAAO;IACvC,OAAM;IACN,QACE,kBAAC,KAAD;KAAG,WAAU;eACX,kBAAC,UAAD;MACE,MAAK;MACL,eAAe,EAAW,WAAW;MACrC,WAAU;gBACX;MAEQ,CAAA;KACP,CAAA;IAEN,CAAA;GAEL;EACD;GACE,IAAI;GACJ,MACE,kBAAC,GAAD;IACE,MAAM,kBAAC,GAAD,EAAO,WAAU,WAAY,CAAA;IACnC,OAAO,EAAa,GAAkB;IACtC,OAAM;IACN,QAAQ,IAAgB,YAAY,KAAA;IACpC,QACE,IACE,kBAAC,UAAD;KACE,MAAK;KACL,eAAe,EAAW,SAAS,EAAc,KAAK;KACtD,WAAU;eACX;KAEQ,CAAA,GAET,kBAAC,KAAD;KAAG,WAAU;eAAb;MACE,kBAAC,QAAD;OAAM,WAAU;iBAAoC,EAAiB;OAAc,CAAA;MAAC;MAAI;MAEtF;;IAGR,CAAA;GAEL;EACD;GACE,IAAI;GACJ,MACE,kBAAC,GAAD;IACE,MAAM,kBAAC,GAAD,EAAQ,WAAU,WAAY,CAAA;IACpC,OAAO,EAAa,EAAS,OAAO;IACpC,OAAM;IACN,QACE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,GAAD;MAAS,OAAO;MAAiB,QAAQ;MAAG,cAAa;MAAyB,CAAA,EAClF,kBAAC,KAAD;MAAG,WAAU;gBAAb;OACE,kBAAC,QAAD;QAAM,WAAU;kBACb,EAAc,EAAgB;QAC1B,CAAA;OAAC;OAAI;OAEV;QACA;;IAER,CAAA;GAEL;EACD;GACE,IAAI;GACJ,MACE,kBAAC,GAAD;IACE,MAAM,kBAAC,IAAD,EAAQ,WAAU,WAAY,CAAA;IACpC,OAAO,IAAe,EAAa,EAAa,MAAM,GAAG;IACzD,OAAM;IACN,QACE,KAAe,OAeb,kBAAC,UAAD;KACE,MAAK;KACL,eAAe,EAAW,WAAW;KACrC,WAAU;eACX;KAEQ,CAAA,GApBT,kBAAC,KAAD;KAAG,WAAU;eAAb;MACE,kBAAC,QAAD;OACE,WACE,KAAe,IACX,yCACA;iBAJR;QAOG,IAAc,IAAI,MAAM;QACxB,EAAY,QAAQ,EAAE;QAAC;QACnB;;MAAC;MAAI;MAEV;;IAWR,CAAA;GAEL;EACD;GACE,IAAI;GACJ,MACE,kBAAC,GAAD;IACE,MAAM,kBAAC,GAAD,EAAM,WAAU,WAAY,CAAA;IAClC,OAAO,IAAkB,GAAG,EAAgB,UAAU;IACtD,OAAM;IACN,QACE,IACI,EAAgB,SAAS,KACvB,YACA,EAAgB,SAAS,KACvB,YACA,UACJ,KAAA;IAEN,QACE,IACE,kBAAC,KAAD;KAAG,WAAU;eACV,EAAmB,EAAgB,SAAS;KAC3C,CAAA,GAEJ,kBAAC,UAAD;KACE,MAAK;KACL,eAAe,EAAW,WAAW;KACrC,WAAU;eACX;KAEQ,CAAA;IAGb,CAAA;GAEL;EACF;AAED,QACE,kBAAC,GAAD;EACE,OAAM;EACN,eAAA;EACA,QAAA;EACA,aAAa,iBAAiB,GAAa;YAJ7C;GAOE,kBAAC,OAAD;IACE,aAAU;IACV,WAAU;cAET,GAAS,KAAK,GAAM,MACnB,kBAAC,EAAO,KAAR;KAEE,WAAU;KACV,GAAI,EAAK,EAAE;KACX,GAAI;eAJN,CAOE,kBAAC,QAAD;MACE,eAAA;MACA,WAAU;MACV,CAAA,EACD,EAAK,KACK;OAXN,EAAK,GAWC,CACb;IACE,CAAA;GAEN,kBAAC,OAAD;IAAK,WAAU;cAAf,CAEE,kBAAC,EAAO,KAAR;KACE,aAAU;KACV,WAAU;KACV,GAAI,EAAK,EAAE;eAEX,kBAAC,GAAD;MAAW,WAAU;MAAQ,WAAU;gBAAvC,CACE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,MAAD;QAAI,WAAU;kBAA4C;QAErD,CAAA,EACL,kBAAC,KAAD;QAAG,WAAU;kBAAqC;QAE9C,CAAA,CACA,EAAA,CAAA,EACN,kBAAC,UAAD;QACE,MAAK;QACL,eAAe,EAAW,YAAY;QACtC,WAAU;kBAHZ,CAIC,cACW,kBAAC,GAAD,EAAY,WAAU,WAAY,CAAA,CACrC;UACL;UACL,CAAC,KAAc,EAAU,kBAAkB,KAAK,EAAU,aAAa,IACtE,kBAAC,GAAD;OACE,cAAa;OACb,MAAM,kBAAC,IAAD,EAAO,WAAU,aAAc,CAAA;OACrC,OAAM;OACN,aAAY;OACZ,CAAA,GAEF,kBAAC,OAAD;OAAK,WAAU;iBAAf,CAEE,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,QAAD;SACE,eAAA;SACA,WAAU;SACV,OAAO,EACL,YACE,iHACH;SACD,CAAA,EACF,kBAAC,IAAD;SAAc,OAAO;SAAY,MAAM;SAAK,QAAQ;mBAApD;UACE,kBAAC,QAAD;WAAM,WAAU;qBAAqE;WAE9E,CAAA;UACP,kBAAC,QAAD;WAAM,WAAU;qBACb,EAAa,EAAU,SAAS;WAC5B,CAAA;UACP,kBAAC,QAAD;WAAM,WAAU;qBAAhB;YAAoD;YAC/C,EAAa,EAAU,cAAc;YAAC;YACpC;;UACM;WACX;WACN,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,IAAD;UACE,UAAU,EAAU;UACpB,QAAQ,EAAU;UAClB,MAAM,EAAU;UAChB,QAAQ;UACR,CAAA,EACF,kBAAC,IAAD,EAAe,CAAA,CACX;YACN,kBAAC,OAAD;SAAK,WAAU;mBAEX;UACE;WAAC;WAAW,EAAU;WAAU,EAAU;WAAc;UACxD;WAAC;WAAS,EAAU;WAAQ,EAAU;WAAY;UAClD;WAAC;WAAO,EAAU;WAAM,EAAU;WAAU;UAC7C,CACD,KAAK,CAAC,GAAO,GAAK,OAAY;UAC9B,IAAM,IAAM,IAAU,IAAM,IAAU,MAAM;AAC5C,iBACE,kBAAC,OAAD;WAEE,WAAU;qBAFZ;YAIE,kBAAC,OAAD;aAAK,WAAU;uBAAf,CACE,kBAAC,KAAD;cAAG,WAAU;wBAA0C;cAAU,CAAA,EACjE,kBAAC,QAAD;cAAM,WAAU;wBACb,EAAc,EAAI;cACd,CAAA,CACH;;YACN,kBAAC,KAAD;aAAG,WAAU;uBAAb,CACG,EAAa,EAAI,EAClB,kBAAC,QAAD;cAAM,WAAU;wBAAhB;eACG;eAAI;eACF,EAAa,EAAO;eAAC;eACnB;gBACL;;YACJ,kBAAC,GAAD;aAAO,OAAO;aAAK,QAAQ;aAAG,WAAU;aAAS,CAAA;YAC7C;aAjBC,EAiBD;WAER;SACE,CAAA,CACF;UACF;SAEE;;KACD,CAAA,EAGb,kBAAC,EAAO,KAAR;KAAY,aAAU;KAAiC,GAAI,EAAK,EAAE;eAChE,kBAAC,GAAD;MAAW,WAAU;MAAQ,WAAU;gBAAvC,CACE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,MAAD;QAAI,WAAU;kBAA4C;QAAY,CAAA,EACtE,kBAAC,UAAD;QACE,MAAK;QACL,eAAe,EAAW,QAAQ;QAClC,WAAU;kBAHZ,CAIC,UACO,kBAAC,GAAD,EAAY,WAAU,WAAY,CAAA,CACjC;UACL;UACL,IACC,kBAAC,EAAO,QAAR;OACE,MAAK;OACL,eAAe,EAAW,SAAS,EAAc,KAAK;OACtD,WAAU;OACV,GAAI;iBAJN;QAME,kBAAC,OAAD;SAAK,WAAU;mBACb,kBAAC,GAAD,EAAa,QAAQ,EAAc,QAAU,CAAA;SACzC,CAAA;QACN,kBAAC,KAAD;SAAG,WAAU;mBAA6C;SAEtD,CAAA;QACJ,kBAAC,KAAD;SAAG,WAAU;mBAAb,CAAiD,YACtC,EAAmB,EAAc,UAAU,CAClD;;QACU;WACd,IACF,kBAAC,EAAO,QAAR;OACE,MAAK;OACL,eAAe,EAAW,SAAS,EAAgB,KAAK;OACxD,WAAU;OACV,GAAI;iBAEJ,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,GAAD,EAAU,MAAM,kBAAC,IAAD,EAAe,WAAU,WAAY,CAAA,EAAI,CAAA,EACzD,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;SAAG,WAAU;mBAAwC;SAAqB,CAAA,EAC1E,kBAAC,KAAD;SAAG,WAAU;mBACV,EAAmB,EAAgB,aAAa;SAC/C,CAAA,CACA,EAAA,CAAA,CACF;;OACQ,CAAA,GAEhB,kBAAC,OAAD;OAAK,WAAU;iBAAf;QACE,kBAAC,QAAD;SAAM,WAAU;mBACd,kBAAC,GAAD,EAAO,WAAU,uBAAwB,CAAA;SACpC,CAAA;QACP,kBAAC,KAAD;SAAG,WAAU;mBAA6C;SAAwB,CAAA;QAClF,kBAAC,UAAD;SACE,MAAK;SACL,eAAe,EAAW,QAAQ;SAClC,WAAU;mBACX;SAEQ,CAAA;QACL;SAEE;;KACD,CAAA,CACT;;GAGN,kBAAC,GAAD;IACE,aAAU;IACV,OAAM;IACN,aAAY;IACZ,QACE,kBAAC,UAAD;KACE,MAAK;KACL,eAAe,EAAW,QAAQ;KAClC,WAAU;eAHZ,CAIC,cACW,kBAAC,GAAD,EAAY,WAAU,WAAY,CAAA,CACrC;;cAGV,EAAS,WAAW,IACnB,kBAAC,GAAD;KACE,cAAa;KACb,MAAM,kBAAC,GAAD,EAAQ,WAAU,aAAc,CAAA;KACtC,OAAM;KACN,aAAY;KACZ,CAAA,GAEF,kBAAC,OAAD;KAAK,WAAU;eACZ,EAAS,MAAM,GAAG,EAAE,CAAC,KAAK,GAAG,MAAM;MAClC,IAAM,IAAM,EAAoB,EAAE,EAC5B,IAAO,EAAe,EAAE,OACxB,IAAO,EAAK;AAClB,aACE,kBAAC,EAAO,QAAR;OAEE,MAAK;OACL,eAAe,EAAW,QAAQ;OAClC,WAAU;OACV,GAAI,EAAK,EAAE;OACX,GAAI;iBAEJ,kBAAC,GAAD;QACE,WAAU;QACV,WAAU;kBAFZ,CAIE,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,GAAD;UAAU,MAAM,kBAAC,GAAD,EAAM,WAAU,WAAY,CAAA;UAAE,MAAM,EAAK;UAAQ,CAAA,EACjE,kBAAC,OAAD;UAAK,WAAU;oBAAf,CACE,kBAAC,KAAD;WAAG,WAAU;qBACV,EAAE;WACD,CAAA,EACJ,kBAAC,KAAD;WAAG,WAAU;qBAA8B,EAAgB,EAAE,KAAK;WAAK,CAAA,CACnE;YACF;YACN,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,GAAD,EAAO,OAAO,GAAO,CAAA,EACrB,kBAAC,OAAD;UAAK,WAAU;oBAAf,CACE,kBAAC,QAAD;WAAM,WAAU;qBAAhB;YACG,EAAa,EAAE,aAAa;YAC5B,EAAE,OAAO,IAAI,EAAE,SAAS;YAAG;YAAI,EAAa,EAAE,YAAY;YAC1D,EAAE,OAAO,IAAI,EAAE,SAAS;YACpB;cACP,kBAAC,QAAD;WAAM,WAAU;qBACb,EAAc,EAAI;WACd,CAAA,CACH;YACF;WACI;;OACE,EAlCT,EAAE,GAkCO;OAElB;KACE,CAAA;IAEA,CAAA;GAGV,kBAAC,GAAD;IACE,aAAU;IACV,OAAM;IACN,aAAY;IACZ,QACE,kBAAC,UAAD;KACE,MAAK;KACL,eAAe,EAAW,QAAQ;KAClC,WAAU;eAHZ,CAIC,iBACc,kBAAC,GAAD,EAAY,WAAU,WAAY,CAAA,CACxC;;cAGV,EAAY,WAAW,IACtB,kBAAC,GAAD;KACE,cAAa;KACb,MAAM,kBAAC,GAAD,EAAO,WAAU,aAAc,CAAA;KACrC,OAAM;KACN,aAAY;KACZ,CAAA,GAEF,kBAAC,OAAD;KAAK,WAAU;eACZ,EAAY,MAAM,GAAG,EAAE,CAAC,KAAK,GAAG,MAAM;MACrC,IAAM,IAAO,GAAkB,UACzB,IAAO,EAAE,WAAW,cAAc,IAAe,EAAK;AAC5D,aACE,kBAAC,EAAO,QAAR;OAEE,MAAK;OACL,eAAe,EAAW,SAAS,EAAE,KAAK;OAC1C,WAAU;OACV,GAAI,EAAK,EAAE;OACX,GAAI;iBAEJ,kBAAC,GAAD;QAAW,WAAU;QAAQ,WAAU;kBAAvC;SACE,kBAAC,OAAD;UAAK,WAAU;oBAAf,CACE,kBAAC,GAAD;WAAU,MAAM,kBAAC,GAAD,EAAM,WAAU,WAAY,CAAA;WAAE,MAAM,EAAK;WAAQ,CAAA,EACjE,kBAAC,GAAD,EAAa,QAAQ,EAAE,QAAU,CAAA,CAC7B;;SACN,kBAAC,KAAD;UAAG,WAAU;oBACV,EAAE,iBAAiB,IAChB,GAAG,EAAa,EAAE,eAAe,CAAC,gBAClC;UACF,CAAA;SACJ,kBAAC,KAAD;UAAG,WAAU;oBACV,EAAmB,EAAE,eAAe,EAAE,aAAa,EAAE,UAAU;UAC9D,CAAA;SACM;;OACE,EArBT,EAAE,GAqBO;OAElB;KACE,CAAA;IAEA,CAAA;GACC"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { FC, ReactNode } from 'react';
|
|
2
|
+
import { Dumbbell, Timer, Weight, Target, Gauge } from 'lucide-react';
|
|
3
|
+
import { MybBodyMetricType, MybGoalType, MybMuscleGroup, MybRecoveryMetric, MybWorkoutType } from '../../types';
|
|
4
|
+
|
|
5
|
+
export declare const Meter: FC<{
|
|
6
|
+
/** 0–100 percentage. */
|
|
7
|
+
value: number;
|
|
8
|
+
/** CSS color for the fill (default: brand emerald). */
|
|
9
|
+
color?: string;
|
|
10
|
+
height?: number;
|
|
11
|
+
className?: string;
|
|
12
|
+
}>;
|
|
13
|
+
export declare const ProgressRing: FC<{
|
|
14
|
+
value: number;
|
|
15
|
+
size?: number;
|
|
16
|
+
stroke?: number;
|
|
17
|
+
color?: string;
|
|
18
|
+
trackColor?: string;
|
|
19
|
+
children?: ReactNode;
|
|
20
|
+
className?: string;
|
|
21
|
+
}>;
|
|
22
|
+
export declare const MACRO_HUE: {
|
|
23
|
+
readonly protein: "var(--color-action-primary-bg)";
|
|
24
|
+
readonly carbs: "var(--color-status-warning-text)";
|
|
25
|
+
readonly fat: "var(--color-accent-primary)";
|
|
26
|
+
};
|
|
27
|
+
export declare const MacroBar: FC<{
|
|
28
|
+
proteinG: number;
|
|
29
|
+
carbsG: number;
|
|
30
|
+
fatG: number;
|
|
31
|
+
height?: number;
|
|
32
|
+
className?: string;
|
|
33
|
+
}>;
|
|
34
|
+
export declare const MacroLegend: FC<{
|
|
35
|
+
className?: string;
|
|
36
|
+
}>;
|
|
37
|
+
export declare const WORKOUT_TYPE_META: Record<MybWorkoutType, {
|
|
38
|
+
icon: typeof Dumbbell;
|
|
39
|
+
tint: string;
|
|
40
|
+
}>;
|
|
41
|
+
export declare const MUSCLE_META: Record<MybMuscleGroup, {
|
|
42
|
+
tint: string;
|
|
43
|
+
}>;
|
|
44
|
+
export declare const GOAL_TYPE_META: Record<MybGoalType, {
|
|
45
|
+
icon: typeof Target;
|
|
46
|
+
tint: string;
|
|
47
|
+
}>;
|
|
48
|
+
export declare const METRIC_META: Record<MybBodyMetricType, {
|
|
49
|
+
icon: typeof Weight;
|
|
50
|
+
tint: string;
|
|
51
|
+
unit: string;
|
|
52
|
+
}>;
|
|
53
|
+
export declare const RECOVERY_META: Record<MybRecoveryMetric, {
|
|
54
|
+
icon: typeof Gauge;
|
|
55
|
+
tint: string;
|
|
56
|
+
label: string;
|
|
57
|
+
}>;
|
|
58
|
+
/** Circular icon chip with a soft tinted background. */
|
|
59
|
+
export declare const IconChip: FC<{
|
|
60
|
+
icon: ReactNode;
|
|
61
|
+
tint?: string;
|
|
62
|
+
className?: string;
|
|
63
|
+
}>;
|
|
64
|
+
/** A small trend delta pill (up/down/flat) with semantic color. */
|
|
65
|
+
export declare const TrendPill: FC<{
|
|
66
|
+
delta: number;
|
|
67
|
+
unit?: string;
|
|
68
|
+
goodDirection?: 'up' | 'down';
|
|
69
|
+
}>;
|
|
70
|
+
/** Compute a goal's completion percentage from baseline → target → current. */
|
|
71
|
+
export declare function computeGoalProgress(goal: {
|
|
72
|
+
baselineValue: number;
|
|
73
|
+
targetValue: number;
|
|
74
|
+
currentValue: number;
|
|
75
|
+
}): number;
|
|
76
|
+
export { Timer };
|
|
77
|
+
//# sourceMappingURL=visuals.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"visuals.d.ts","sourceRoot":"","sources":["../../../../src/mybodyshield/pages/_shared/visuals.tsx"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,EAAE,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAC3C,OAAO,EACL,QAAQ,EAER,KAAK,EAKL,MAAM,EAQN,MAAM,EAIN,KAAK,EAEN,MAAM,cAAc,CAAC;AAEtB,OAAO,KAAK,EACV,iBAAiB,EACjB,WAAW,EACX,cAAc,EACd,iBAAiB,EACjB,cAAc,EACf,MAAM,aAAa,CAAC;AAMrB,eAAO,MAAM,KAAK,EAAE,EAAE,CAAC;IACrB,wBAAwB;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,uDAAuD;IACvD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAiBA,CAAC;AAMF,eAAO,MAAM,YAAY,EAAE,EAAE,CAAC;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CA6CA,CAAC;AAMF,eAAO,MAAM,SAAS;;;;CAIZ,CAAC;AAEX,eAAO,MAAM,QAAQ,EAAE,EAAE,CAAC;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAaA,CAAC;AAEF,eAAO,MAAM,WAAW,EAAE,EAAE,CAAC;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,CASlD,CAAC;AA+BF,eAAO,MAAM,iBAAiB;UARiC,OAAO,QAAQ;UAAQ,MAAM;EAQL,CAAC;AAWxF,eAAO,MAAM,WAAW;UATiC,MAAM;EASa,CAAC;AAU7E,eAAO,MAAM,cAAc;UAR8B,OAAO,MAAM;UAAQ,MAAM;EAQI,CAAC;AAezF,eAAO,MAAM,WAAW;UAXd,OAAO,MAAM;UAAQ,MAAM;UAAQ,MAAM;EAWsB,CAAC;AAY1E,eAAO,MAAM,aAAa;UARhB,OAAO,KAAK;UAAQ,MAAM;WAAS,MAAM;EAQ6B,CAAC;AAMjF,wDAAwD;AACxD,eAAO,MAAM,QAAQ,EAAE,EAAE,CAAC;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,CAW/E,CAAC;AAEF,mEAAmE;AACnE,eAAO,MAAM,SAAS,EAAE,EAAE,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,aAAa,CAAC,EAAE,IAAI,GAAG,MAAM,CAAA;CAAE,CAwBzF,CAAC;AAEF,+EAA+E;AAC/E,wBAAgB,mBAAmB,CAAC,IAAI,EAAE;IACxC,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;CACtB,GAAG,MAAM,CAKT;AAED,OAAO,EAAE,KAAK,EAAE,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Coaching — coach ↔ member relationships and shared data scope.
|
|
3
|
+
* @module mybodyshield/pages/coaching
|
|
4
|
+
*
|
|
5
|
+
* Lists the member's `MybCoachingRelationship`s (a PLAIN ARRAY from
|
|
6
|
+
* `mybListCoaching` — no pagination) with a status filter, per-relationship
|
|
7
|
+
* sharing scope (workouts / nutrition / metrics), lifecycle transition actions
|
|
8
|
+
* (accept / pause / resume / end) and a create flow. No mock data.
|
|
9
|
+
*/
|
|
10
|
+
export declare function CoachingPage(): import("react").JSX.Element;
|
|
11
|
+
//# sourceMappingURL=CoachingPage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CoachingPage.d.ts","sourceRoot":"","sources":["../../../../src/mybodyshield/pages/coaching/CoachingPage.tsx"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AA6fH,wBAAgB,YAAY,gCA0I3B"}
|
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
import { useMyBodyShield as e } from "../../context/MyBodyShieldContext.js";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import { ErrorView as s, LoadingView as c } from "../../components/StateViews.js";
|
|
9
|
-
import { EmptyState as l } from "../../components/EmptyState.js";
|
|
10
|
-
import { IconChip as u } from "../_shared/visuals.js";
|
|
2
|
+
import { PageLayout as t } from "../../components/PageLayout.js";
|
|
3
|
+
import { formatDate as n } from "../../utils/formatters.js";
|
|
4
|
+
import { StatusBadge as r } from "../../components/StatusBadge.js";
|
|
5
|
+
import { EmptyState as i } from "../../components/EmptyState.js";
|
|
6
|
+
import { ErrorView as a, LoadingView as o } from "../../components/StateViews.js";
|
|
7
|
+
import { FilterTabs as s } from "../../components/FilterTabs.js";
|
|
11
8
|
import "../../context/index.js";
|
|
12
|
-
import { useMybCoaching as
|
|
9
|
+
import { useMybCoaching as c, useMybCreateCoaching as l, useMybTransitionCoaching as u } from "../../hooks/useMyBodyShieldApi.js";
|
|
13
10
|
import "../../hooks/index.js";
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import { UserSearchSelect as g } from "../../components/UserSearchSelect.js";
|
|
11
|
+
import { MybDiscussion as d } from "../../components/MybDiscussion.js";
|
|
12
|
+
import { UserSearchSelect as f } from "../../components/UserSearchSelect.js";
|
|
17
13
|
import "../../components/index.js";
|
|
14
|
+
import { AnimatePresence as p } from "../../../node_modules/framer-motion/dist/es/components/AnimatePresence/index.js";
|
|
15
|
+
import { motion as m } from "../../../node_modules/framer-motion/dist/es/render/components/motion/proxy.js";
|
|
16
|
+
import { useReducedMotion as h } from "../../../node_modules/framer-motion/dist/es/utils/reduced-motion/use-reduced-motion.js";
|
|
17
|
+
import { IconChip as g } from "../_shared/visuals.js";
|
|
18
18
|
import { useMemo as _, useState as v } from "react";
|
|
19
19
|
import { CalendarClock as y, CalendarX as b, Check as x, ChevronDown as S, ChevronUp as C, Dumbbell as w, HeartHandshake as T, LineChart as E, MessageSquare as D, Minus as O, Plus as k, Salad as A, User as j } from "lucide-react";
|
|
20
20
|
import { jsx as M, jsxs as N } from "react/jsx-runtime";
|
|
@@ -108,9 +108,9 @@ function Q({ id: e, label: t, description: n, checked: r, onChange: i }) {
|
|
|
108
108
|
});
|
|
109
109
|
}
|
|
110
110
|
function ee({ open: e, onOpenChange: t, defaultMember: n }) {
|
|
111
|
-
let r =
|
|
111
|
+
let r = l(), [i, a] = v(() => Z(n)), [o, s] = v(null), c = (e) => {
|
|
112
112
|
e || (a(Z(n)), s(null)), t(e);
|
|
113
|
-
},
|
|
113
|
+
}, u = async () => {
|
|
114
114
|
let e = i.coachId.trim(), o = i.memberId.trim();
|
|
115
115
|
if (!e) {
|
|
116
116
|
s("Coach user id is required.");
|
|
@@ -141,14 +141,14 @@ function ee({ open: e, onOpenChange: t, defaultMember: n }) {
|
|
|
141
141
|
children: /* @__PURE__ */ N(z, { children: [/* @__PURE__ */ N(H, { children: [/* @__PURE__ */ M(U, { children: "New coaching" }), /* @__PURE__ */ M(B, { children: "Link a coach and member, and choose exactly what training, nutrition and progress data is shared." })] }), /* @__PURE__ */ N("form", {
|
|
142
142
|
className: "space-y-4",
|
|
143
143
|
onSubmit: (e) => {
|
|
144
|
-
e.preventDefault(),
|
|
144
|
+
e.preventDefault(), u();
|
|
145
145
|
},
|
|
146
146
|
children: [
|
|
147
147
|
o && /* @__PURE__ */ M("p", {
|
|
148
148
|
className: "rounded-lg bg-status-error-bg-subtle px-3 py-2 text-sm text-status-error-text",
|
|
149
149
|
children: o
|
|
150
150
|
}),
|
|
151
|
-
/* @__PURE__ */ M(
|
|
151
|
+
/* @__PURE__ */ M(f, {
|
|
152
152
|
label: "Coach",
|
|
153
153
|
required: !0,
|
|
154
154
|
htmlId: "myb-coaching-coach",
|
|
@@ -160,7 +160,7 @@ function ee({ open: e, onOpenChange: t, defaultMember: n }) {
|
|
|
160
160
|
coachName: t
|
|
161
161
|
}))
|
|
162
162
|
}),
|
|
163
|
-
/* @__PURE__ */ M(
|
|
163
|
+
/* @__PURE__ */ M(f, {
|
|
164
164
|
label: "Member",
|
|
165
165
|
htmlId: "myb-coaching-member",
|
|
166
166
|
userId: i.memberId,
|
|
@@ -263,8 +263,8 @@ function $({ label: e, icon: t, on: n }) {
|
|
|
263
263
|
]
|
|
264
264
|
});
|
|
265
265
|
}
|
|
266
|
-
function te({ relationship: e, currentUserId:
|
|
267
|
-
let
|
|
266
|
+
function te({ relationship: e, currentUserId: t, busy: i, onTransition: a }) {
|
|
267
|
+
let o = !!t && e.coachId === t, s = !!t && e.memberId === t, c = Y[e.status], l = e.status === "ACTIVE" || e.status === "PAUSED", [u, f] = v(!1), _ = h();
|
|
268
268
|
return /* @__PURE__ */ N(F, {
|
|
269
269
|
treatment: "glass",
|
|
270
270
|
glow: e.status === "ACTIVE",
|
|
@@ -274,7 +274,7 @@ function te({ relationship: e, currentUserId: i, busy: s, onTransition: c }) {
|
|
|
274
274
|
className: "flex items-start justify-between gap-3",
|
|
275
275
|
children: [/* @__PURE__ */ N("div", {
|
|
276
276
|
className: "flex min-w-0 items-start gap-3",
|
|
277
|
-
children: [/* @__PURE__ */ M(
|
|
277
|
+
children: [/* @__PURE__ */ M(g, { icon: /* @__PURE__ */ M(T, { className: "h-4 w-4" }) }), /* @__PURE__ */ N("div", {
|
|
278
278
|
className: "min-w-0",
|
|
279
279
|
children: [/* @__PURE__ */ M("p", {
|
|
280
280
|
className: "truncate text-sm font-semibold text-text-primary",
|
|
@@ -284,7 +284,7 @@ function te({ relationship: e, currentUserId: i, busy: s, onTransition: c }) {
|
|
|
284
284
|
children: "Coach ↔ member"
|
|
285
285
|
})]
|
|
286
286
|
})]
|
|
287
|
-
}), /* @__PURE__ */ M(
|
|
287
|
+
}), /* @__PURE__ */ M(r, { status: e.status })]
|
|
288
288
|
}),
|
|
289
289
|
/* @__PURE__ */ N("div", {
|
|
290
290
|
className: "grid grid-cols-2 gap-3",
|
|
@@ -292,7 +292,7 @@ function te({ relationship: e, currentUserId: i, busy: s, onTransition: c }) {
|
|
|
292
292
|
className: "flex items-center gap-2.5 rounded-lg bg-bg-sunken/60 p-2.5",
|
|
293
293
|
children: [/* @__PURE__ */ M("span", {
|
|
294
294
|
"aria-hidden": "true",
|
|
295
|
-
className: `flex size-8 shrink-0 items-center justify-center rounded-full ring-1 ring-inset ${
|
|
295
|
+
className: `flex size-8 shrink-0 items-center justify-center rounded-full ring-1 ring-inset ${o ? "bg-[color-mix(in_oklab,var(--color-action-primary-bg)_16%,transparent)] text-action-primary-bg ring-[color-mix(in_oklab,var(--color-action-primary-bg)_40%,transparent)]" : "bg-bg-surface text-text-tertiary ring-border-seam"}`,
|
|
296
296
|
children: /* @__PURE__ */ M(j, { className: "h-4 w-4" })
|
|
297
297
|
}), /* @__PURE__ */ N("div", {
|
|
298
298
|
className: "min-w-0",
|
|
@@ -302,14 +302,14 @@ function te({ relationship: e, currentUserId: i, busy: s, onTransition: c }) {
|
|
|
302
302
|
}), /* @__PURE__ */ M("p", {
|
|
303
303
|
className: "mt-0.5 truncate text-sm font-medium text-text-primary",
|
|
304
304
|
title: e.coachId,
|
|
305
|
-
children:
|
|
305
|
+
children: o ? "You" : `#${X(e.coachId)}`
|
|
306
306
|
})]
|
|
307
307
|
})]
|
|
308
308
|
}), /* @__PURE__ */ N("div", {
|
|
309
309
|
className: "flex items-center gap-2.5 rounded-lg bg-bg-sunken/60 p-2.5",
|
|
310
310
|
children: [/* @__PURE__ */ M("span", {
|
|
311
311
|
"aria-hidden": "true",
|
|
312
|
-
className: `flex size-8 shrink-0 items-center justify-center rounded-full ring-1 ring-inset ${
|
|
312
|
+
className: `flex size-8 shrink-0 items-center justify-center rounded-full ring-1 ring-inset ${s ? "bg-[color-mix(in_oklab,var(--color-action-primary-bg)_16%,transparent)] text-action-primary-bg ring-[color-mix(in_oklab,var(--color-action-primary-bg)_40%,transparent)]" : "bg-bg-surface text-text-tertiary ring-border-seam"}`,
|
|
313
313
|
children: /* @__PURE__ */ M(j, { className: "h-4 w-4" })
|
|
314
314
|
}), /* @__PURE__ */ N("div", {
|
|
315
315
|
className: "min-w-0",
|
|
@@ -319,7 +319,7 @@ function te({ relationship: e, currentUserId: i, busy: s, onTransition: c }) {
|
|
|
319
319
|
}), /* @__PURE__ */ M("p", {
|
|
320
320
|
className: "mt-0.5 truncate text-sm font-medium text-text-primary",
|
|
321
321
|
title: e.memberId,
|
|
322
|
-
children:
|
|
322
|
+
children: s ? "You" : `#${X(e.memberId)}`
|
|
323
323
|
})]
|
|
324
324
|
})]
|
|
325
325
|
})]
|
|
@@ -351,42 +351,42 @@ function te({ relationship: e, currentUserId: i, busy: s, onTransition: c }) {
|
|
|
351
351
|
children: [
|
|
352
352
|
/* @__PURE__ */ M(y, { className: "h-3.5 w-3.5" }),
|
|
353
353
|
"Started ",
|
|
354
|
-
|
|
354
|
+
n(e.startedAt)
|
|
355
355
|
]
|
|
356
356
|
}), e.endedAt && /* @__PURE__ */ N("span", {
|
|
357
357
|
className: "inline-flex items-center gap-1",
|
|
358
358
|
children: [
|
|
359
359
|
/* @__PURE__ */ M(b, { className: "h-3.5 w-3.5" }),
|
|
360
360
|
"Ended ",
|
|
361
|
-
|
|
361
|
+
n(e.endedAt)
|
|
362
362
|
]
|
|
363
363
|
})]
|
|
364
364
|
}),
|
|
365
|
-
|
|
365
|
+
c.length > 0 && /* @__PURE__ */ M("div", {
|
|
366
366
|
className: "mt-auto flex flex-wrap gap-2 pt-1",
|
|
367
|
-
children:
|
|
367
|
+
children: c.map((t) => /* @__PURE__ */ M(P, {
|
|
368
368
|
size: "sm",
|
|
369
369
|
variant: t.primary ? void 0 : "outline",
|
|
370
|
-
disabled:
|
|
371
|
-
onClick: () =>
|
|
370
|
+
disabled: i,
|
|
371
|
+
onClick: () => a(e.id, t.to),
|
|
372
372
|
children: t.label
|
|
373
373
|
}, t.to))
|
|
374
374
|
}),
|
|
375
|
-
|
|
376
|
-
className: `border-t border-border-seam pt-3 ${
|
|
375
|
+
l && /* @__PURE__ */ N("div", {
|
|
376
|
+
className: `border-t border-border-seam pt-3 ${c.length === 0 ? "mt-auto" : ""}`,
|
|
377
377
|
children: [/* @__PURE__ */ N("button", {
|
|
378
378
|
type: "button",
|
|
379
|
-
onClick: () =>
|
|
380
|
-
"aria-expanded":
|
|
379
|
+
onClick: () => f((e) => !e),
|
|
380
|
+
"aria-expanded": u,
|
|
381
381
|
className: "inline-flex items-center gap-1.5 text-sm font-medium text-accent hover:underline",
|
|
382
382
|
children: [
|
|
383
383
|
/* @__PURE__ */ M(D, { className: "h-4 w-4" }),
|
|
384
|
-
|
|
385
|
-
M(
|
|
384
|
+
u ? "Hide discussion" : "Discussion",
|
|
385
|
+
M(u ? C : S, { className: "h-4 w-4" })
|
|
386
386
|
]
|
|
387
|
-
}), /* @__PURE__ */ M(
|
|
387
|
+
}), /* @__PURE__ */ M(p, {
|
|
388
388
|
initial: !1,
|
|
389
|
-
children:
|
|
389
|
+
children: u && /* @__PURE__ */ M(m.div, {
|
|
390
390
|
initial: _ ? !1 : {
|
|
391
391
|
opacity: 0,
|
|
392
392
|
height: 0
|
|
@@ -406,7 +406,7 @@ function te({ relationship: e, currentUserId: i, busy: s, onTransition: c }) {
|
|
|
406
406
|
className: "overflow-hidden",
|
|
407
407
|
children: /* @__PURE__ */ M("div", {
|
|
408
408
|
className: "mt-3",
|
|
409
|
-
children: /* @__PURE__ */ M(
|
|
409
|
+
children: /* @__PURE__ */ M(d, {
|
|
410
410
|
entityType: "mybodyshield-coaching",
|
|
411
411
|
entityId: e.id,
|
|
412
412
|
title: "Coaching discussion",
|
|
@@ -420,7 +420,7 @@ function te({ relationship: e, currentUserId: i, busy: s, onTransition: c }) {
|
|
|
420
420
|
});
|
|
421
421
|
}
|
|
422
422
|
function ne() {
|
|
423
|
-
let { currentUser:
|
|
423
|
+
let { currentUser: n } = e(), r = h(), [l, d] = v("ALL"), [f, p] = v(!1), [g, y] = v(null), [b, x] = v(null), S = c(), C = u(), w = S.data ?? [], E = _(() => {
|
|
424
424
|
let e = {
|
|
425
425
|
PENDING: 0,
|
|
426
426
|
ACTIVE: 0,
|
|
@@ -437,7 +437,7 @@ function ne() {
|
|
|
437
437
|
value: e,
|
|
438
438
|
label: e.charAt(0) + e.slice(1).toLowerCase(),
|
|
439
439
|
count: E[e]
|
|
440
|
-
}))], [w.length, E]), O = _(() =>
|
|
440
|
+
}))], [w.length, E]), O = _(() => l === "ALL" ? w : w.filter((e) => e.status === l), [w, l]), A = async (e, t) => {
|
|
441
441
|
y(e), x(null);
|
|
442
442
|
try {
|
|
443
443
|
await C.mutateAsync({
|
|
@@ -450,49 +450,49 @@ function ne() {
|
|
|
450
450
|
y(null);
|
|
451
451
|
}
|
|
452
452
|
}, j = S.isLoading && !S.data;
|
|
453
|
-
return /* @__PURE__ */ N(
|
|
453
|
+
return /* @__PURE__ */ N(t, {
|
|
454
454
|
title: "Coaching",
|
|
455
455
|
description: "Coach ↔ member relationships and what's shared.",
|
|
456
456
|
actions: /* @__PURE__ */ N(P, {
|
|
457
|
-
onClick: () =>
|
|
457
|
+
onClick: () => p(!0),
|
|
458
458
|
children: [/* @__PURE__ */ M(k, { className: "mr-2 h-4 w-4" }), "New coaching"]
|
|
459
459
|
}),
|
|
460
460
|
children: [
|
|
461
461
|
/* @__PURE__ */ M("div", {
|
|
462
462
|
className: "mb-6",
|
|
463
|
-
children: /* @__PURE__ */ M(
|
|
463
|
+
children: /* @__PURE__ */ M(s, {
|
|
464
464
|
options: D,
|
|
465
|
-
value:
|
|
466
|
-
onChange:
|
|
465
|
+
value: l,
|
|
466
|
+
onChange: d
|
|
467
467
|
})
|
|
468
468
|
}),
|
|
469
469
|
b && /* @__PURE__ */ M("p", {
|
|
470
470
|
className: "mb-4 rounded-lg bg-status-error-bg-subtle px-3 py-2 text-sm text-status-error-text",
|
|
471
471
|
children: b
|
|
472
472
|
}),
|
|
473
|
-
j ? /* @__PURE__ */ M(
|
|
473
|
+
j ? /* @__PURE__ */ M(o, { label: "Loading coaching relationships…" }) : S.isError ? /* @__PURE__ */ M(a, {
|
|
474
474
|
message: "We couldn't load your coaching relationships.",
|
|
475
475
|
onRetry: () => void S.refetch()
|
|
476
|
-
}) : O.length === 0 ? /* @__PURE__ */ M(
|
|
476
|
+
}) : O.length === 0 ? /* @__PURE__ */ M(i, {
|
|
477
477
|
illustration: "empty-data",
|
|
478
478
|
icon: /* @__PURE__ */ M(T, { className: "h-10 w-10" }),
|
|
479
479
|
title: w.length === 0 ? "No coaching relationships" : "No relationships match this filter",
|
|
480
480
|
description: w.length === 0 ? "Connect a coach and a member to start sharing training, nutrition and progress data." : "Try a different status filter, or create a new coaching relationship.",
|
|
481
481
|
action: /* @__PURE__ */ N(P, {
|
|
482
|
-
onClick: () =>
|
|
482
|
+
onClick: () => p(!0),
|
|
483
483
|
children: [/* @__PURE__ */ M(k, { className: "mr-2 h-4 w-4" }), "New coaching"]
|
|
484
484
|
})
|
|
485
|
-
}) : /* @__PURE__ */ M(
|
|
485
|
+
}) : /* @__PURE__ */ M(m.div, {
|
|
486
486
|
className: "grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3",
|
|
487
|
-
variants:
|
|
488
|
-
initial:
|
|
487
|
+
variants: r ? void 0 : q,
|
|
488
|
+
initial: r ? !1 : "hidden",
|
|
489
489
|
animate: "show",
|
|
490
|
-
children: O.map((e) => /* @__PURE__ */ M(
|
|
490
|
+
children: O.map((e) => /* @__PURE__ */ M(m.div, {
|
|
491
491
|
className: "h-full",
|
|
492
|
-
variants:
|
|
492
|
+
variants: r ? void 0 : J,
|
|
493
493
|
children: /* @__PURE__ */ M(te, {
|
|
494
494
|
relationship: e,
|
|
495
|
-
currentUserId:
|
|
495
|
+
currentUserId: n?.id,
|
|
496
496
|
busy: g === e.id,
|
|
497
497
|
onTransition: (e, t) => void A(e, t)
|
|
498
498
|
})
|
|
@@ -500,10 +500,10 @@ function ne() {
|
|
|
500
500
|
}),
|
|
501
501
|
/* @__PURE__ */ M(ee, {
|
|
502
502
|
open: f,
|
|
503
|
-
onOpenChange:
|
|
503
|
+
onOpenChange: p,
|
|
504
504
|
defaultMember: {
|
|
505
|
-
userId:
|
|
506
|
-
displayName:
|
|
505
|
+
userId: n?.id ?? "",
|
|
506
|
+
displayName: n?.name || n?.email || ""
|
|
507
507
|
}
|
|
508
508
|
})
|
|
509
509
|
]
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Goals — targets across training, nutrition and body composition.
|
|
3
|
+
* @module mybodyshield/pages/goals
|
|
4
|
+
*
|
|
5
|
+
* Lists the member's `MybGoal`s with a status filter, per-goal progress meters
|
|
6
|
+
* and inline "update progress" / "mark achieved" actions, plus a create flow.
|
|
7
|
+
* KPIs (active count, achieved count, average active progress) are aggregated
|
|
8
|
+
* client-side from the real `mybListGoals` endpoint. No mock data.
|
|
9
|
+
*/
|
|
10
|
+
export declare function GoalsPage(): import("react").JSX.Element;
|
|
11
|
+
//# sourceMappingURL=GoalsPage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"GoalsPage.d.ts","sourceRoot":"","sources":["../../../../src/mybodyshield/pages/goals/GoalsPage.tsx"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AA4fH,wBAAgB,SAAS,gCAoKxB"}
|