@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.
Files changed (152) hide show
  1. package/dist/dev/main.d.ts +2 -0
  2. package/dist/dev/main.d.ts.map +1 -0
  3. package/dist/generated/global-operations.d.ts +2 -0
  4. package/dist/generated/global-operations.d.ts.map +1 -0
  5. package/dist/generated/global-types.d.ts +18199 -0
  6. package/dist/generated/global-types.d.ts.map +1 -0
  7. package/dist/generated/wspace-operations.d.ts +2 -0
  8. package/dist/generated/wspace-operations.d.ts.map +1 -0
  9. package/dist/generated/wspace-types.d.ts +43046 -0
  10. package/dist/generated/wspace-types.d.ts.map +1 -0
  11. package/dist/index.d.ts +3 -0
  12. package/dist/index.d.ts.map +1 -0
  13. package/dist/index.js +2 -1
  14. package/dist/mybodyshield/MyBodyShieldRoot.d.ts +5 -0
  15. package/dist/mybodyshield/MyBodyShieldRoot.d.ts.map +1 -0
  16. package/dist/mybodyshield/MyBodyShieldRoutes.d.ts +4 -0
  17. package/dist/mybodyshield/MyBodyShieldRoutes.d.ts.map +1 -0
  18. package/dist/mybodyshield/MyBodyShieldRoutes.js +77 -36
  19. package/dist/mybodyshield/MyBodyShieldRoutes.js.map +1 -1
  20. package/dist/mybodyshield/components/DataTable.d.ts +28 -0
  21. package/dist/mybodyshield/components/DataTable.d.ts.map +1 -0
  22. package/dist/mybodyshield/components/EmptyState.d.ts +18 -0
  23. package/dist/mybodyshield/components/EmptyState.d.ts.map +1 -0
  24. package/dist/mybodyshield/components/EvidenceUpload.d.ts +14 -0
  25. package/dist/mybodyshield/components/EvidenceUpload.d.ts.map +1 -0
  26. package/dist/mybodyshield/components/FeatureFlagGuard.d.ts +11 -0
  27. package/dist/mybodyshield/components/FeatureFlagGuard.d.ts.map +1 -0
  28. package/dist/mybodyshield/components/FeatureFlagGuard.js +27 -0
  29. package/dist/mybodyshield/components/FeatureFlagGuard.js.map +1 -0
  30. package/dist/mybodyshield/components/FileLink.d.ts +15 -0
  31. package/dist/mybodyshield/components/FileLink.d.ts.map +1 -0
  32. package/dist/mybodyshield/components/FilterTabs.d.ts +18 -0
  33. package/dist/mybodyshield/components/FilterTabs.d.ts.map +1 -0
  34. package/dist/mybodyshield/components/MetricsCard.d.ts +23 -0
  35. package/dist/mybodyshield/components/MetricsCard.d.ts.map +1 -0
  36. package/dist/mybodyshield/components/MiniBar.d.ts +16 -0
  37. package/dist/mybodyshield/components/MiniBar.d.ts.map +1 -0
  38. package/dist/mybodyshield/components/MybDiscussion.d.ts +18 -0
  39. package/dist/mybodyshield/components/MybDiscussion.d.ts.map +1 -0
  40. package/dist/mybodyshield/components/MybDiscussion.js +5 -5
  41. package/dist/mybodyshield/components/MybTags.d.ts +17 -0
  42. package/dist/mybodyshield/components/MybTags.d.ts.map +1 -0
  43. package/dist/mybodyshield/components/PageDisabled.d.ts +9 -0
  44. package/dist/mybodyshield/components/PageDisabled.d.ts.map +1 -0
  45. package/dist/mybodyshield/components/PageDisabled.js +17 -0
  46. package/dist/mybodyshield/components/PageDisabled.js.map +1 -0
  47. package/dist/mybodyshield/components/PageLayout.d.ts +16 -0
  48. package/dist/mybodyshield/components/PageLayout.d.ts.map +1 -0
  49. package/dist/mybodyshield/components/Pagination.d.ts +22 -0
  50. package/dist/mybodyshield/components/Pagination.d.ts.map +1 -0
  51. package/dist/mybodyshield/components/StateViews.d.ts +25 -0
  52. package/dist/mybodyshield/components/StateViews.d.ts.map +1 -0
  53. package/dist/mybodyshield/components/StateViews.js +2 -1
  54. package/dist/mybodyshield/components/StateViews.js.map +1 -1
  55. package/dist/mybodyshield/components/StatusBadge.d.ts +9 -0
  56. package/dist/mybodyshield/components/StatusBadge.d.ts.map +1 -0
  57. package/dist/mybodyshield/components/Tabs.d.ts +17 -0
  58. package/dist/mybodyshield/components/Tabs.d.ts.map +1 -0
  59. package/dist/mybodyshield/components/UserSearchSelect.d.ts +28 -0
  60. package/dist/mybodyshield/components/UserSearchSelect.d.ts.map +1 -0
  61. package/dist/mybodyshield/components/index.d.ts +22 -0
  62. package/dist/mybodyshield/components/index.d.ts.map +1 -0
  63. package/dist/mybodyshield/components/index.js +4 -2
  64. package/dist/mybodyshield/context/MyBodyShieldContext.d.ts +16 -0
  65. package/dist/mybodyshield/context/MyBodyShieldContext.d.ts.map +1 -0
  66. package/dist/mybodyshield/context/index.d.ts +6 -0
  67. package/dist/mybodyshield/context/index.d.ts.map +1 -0
  68. package/dist/mybodyshield/hooks/index.d.ts +12 -0
  69. package/dist/mybodyshield/hooks/index.d.ts.map +1 -0
  70. package/dist/mybodyshield/hooks/index.js +1 -0
  71. package/dist/mybodyshield/hooks/useMyBodyShieldAnalytics.d.ts +21 -0
  72. package/dist/mybodyshield/hooks/useMyBodyShieldAnalytics.d.ts.map +1 -0
  73. package/dist/mybodyshield/hooks/useMyBodyShieldAnalytics.js +35 -0
  74. package/dist/mybodyshield/hooks/useMyBodyShieldAnalytics.js.map +1 -0
  75. package/dist/mybodyshield/hooks/useMyBodyShieldApi.d.ts +83 -0
  76. package/dist/mybodyshield/hooks/useMyBodyShieldApi.d.ts.map +1 -0
  77. package/dist/mybodyshield/hooks/useMybComments.d.ts +36 -0
  78. package/dist/mybodyshield/hooks/useMybComments.d.ts.map +1 -0
  79. package/dist/mybodyshield/hooks/useMybFiles.d.ts +47 -0
  80. package/dist/mybodyshield/hooks/useMybFiles.d.ts.map +1 -0
  81. package/dist/mybodyshield/hooks/useMybTags.d.ts +44 -0
  82. package/dist/mybodyshield/hooks/useMybTags.d.ts.map +1 -0
  83. package/dist/mybodyshield/hooks/useMybUsers.d.ts +27 -0
  84. package/dist/mybodyshield/hooks/useMybUsers.d.ts.map +1 -0
  85. package/dist/mybodyshield/hooks/usePager.d.ts +29 -0
  86. package/dist/mybodyshield/hooks/usePager.d.ts.map +1 -0
  87. package/dist/mybodyshield/index.d.ts +9 -0
  88. package/dist/mybodyshield/index.d.ts.map +1 -0
  89. package/dist/mybodyshield/pages/DashboardPage.d.ts +10 -0
  90. package/dist/mybodyshield/pages/DashboardPage.d.ts.map +1 -0
  91. package/dist/mybodyshield/pages/DashboardPage.js +91 -86
  92. package/dist/mybodyshield/pages/DashboardPage.js.map +1 -1
  93. package/dist/mybodyshield/pages/_shared/visuals.d.ts +77 -0
  94. package/dist/mybodyshield/pages/_shared/visuals.d.ts.map +1 -0
  95. package/dist/mybodyshield/pages/coaching/CoachingPage.d.ts +11 -0
  96. package/dist/mybodyshield/pages/coaching/CoachingPage.d.ts.map +1 -0
  97. package/dist/mybodyshield/pages/coaching/CoachingPage.js +60 -60
  98. package/dist/mybodyshield/pages/goals/GoalsPage.d.ts +11 -0
  99. package/dist/mybodyshield/pages/goals/GoalsPage.d.ts.map +1 -0
  100. package/dist/mybodyshield/pages/goals/GoalsPage.js +125 -125
  101. package/dist/mybodyshield/pages/goals/GoalsPage.js.map +1 -1
  102. package/dist/mybodyshield/pages/nutrition/NutritionPage.d.ts +13 -0
  103. package/dist/mybodyshield/pages/nutrition/NutritionPage.d.ts.map +1 -0
  104. package/dist/mybodyshield/pages/nutrition/NutritionPage.js +129 -129
  105. package/dist/mybodyshield/pages/nutrition/NutritionPage.js.map +1 -1
  106. package/dist/mybodyshield/pages/programs/ProgramDetailPage.d.ts +10 -0
  107. package/dist/mybodyshield/pages/programs/ProgramDetailPage.d.ts.map +1 -0
  108. package/dist/mybodyshield/pages/programs/ProgramDetailPage.js +218 -191
  109. package/dist/mybodyshield/pages/programs/ProgramDetailPage.js.map +1 -1
  110. package/dist/mybodyshield/pages/programs/ProgramsPage.d.ts +11 -0
  111. package/dist/mybodyshield/pages/programs/ProgramsPage.d.ts.map +1 -0
  112. package/dist/mybodyshield/pages/programs/ProgramsPage.js +197 -193
  113. package/dist/mybodyshield/pages/programs/ProgramsPage.js.map +1 -1
  114. package/dist/mybodyshield/pages/progress/ProgressPage.d.ts +13 -0
  115. package/dist/mybodyshield/pages/progress/ProgressPage.d.ts.map +1 -0
  116. package/dist/mybodyshield/pages/progress/ProgressPage.js +143 -143
  117. package/dist/mybodyshield/pages/progress/ProgressPage.js.map +1 -1
  118. package/dist/mybodyshield/pages/recovery/RecoveryPage.d.ts +11 -0
  119. package/dist/mybodyshield/pages/recovery/RecoveryPage.d.ts.map +1 -0
  120. package/dist/mybodyshield/pages/recovery/RecoveryPage.js +60 -60
  121. package/dist/mybodyshield/pages/recovery/RecoveryPage.js.map +1 -1
  122. package/dist/mybodyshield/pages/train/SessionDetailPage.d.ts +11 -0
  123. package/dist/mybodyshield/pages/train/SessionDetailPage.d.ts.map +1 -0
  124. package/dist/mybodyshield/pages/train/SessionDetailPage.js +184 -181
  125. package/dist/mybodyshield/pages/train/SessionDetailPage.js.map +1 -1
  126. package/dist/mybodyshield/pages/train/SessionsPage.d.ts +11 -0
  127. package/dist/mybodyshield/pages/train/SessionsPage.d.ts.map +1 -0
  128. package/dist/mybodyshield/pages/train/SessionsPage.js +119 -115
  129. package/dist/mybodyshield/pages/train/SessionsPage.js.map +1 -1
  130. package/dist/mybodyshield/services/createSandboxAssistantTransport.d.ts +24 -0
  131. package/dist/mybodyshield/services/createSandboxAssistantTransport.d.ts.map +1 -0
  132. package/dist/mybodyshield/services/createSandboxAssistantTransport.js +14 -0
  133. package/dist/mybodyshield/services/createSandboxAssistantTransport.js.map +1 -0
  134. package/dist/mybodyshield/types/assistant.d.ts +55 -0
  135. package/dist/mybodyshield/types/assistant.d.ts.map +1 -0
  136. package/dist/mybodyshield/types/index.d.ts +419 -0
  137. package/dist/mybodyshield/types/index.d.ts.map +1 -0
  138. package/dist/mybodyshield/utils/cn.d.ts +4 -0
  139. package/dist/mybodyshield/utils/cn.d.ts.map +1 -0
  140. package/dist/mybodyshield/utils/formatters.d.ts +32 -0
  141. package/dist/mybodyshield/utils/formatters.d.ts.map +1 -0
  142. package/dist/mybodyshield/utils/i18n.d.ts +6 -0
  143. package/dist/mybodyshield/utils/i18n.d.ts.map +1 -0
  144. package/dist/mybodyshield/utils/index.d.ts +9 -0
  145. package/dist/mybodyshield/utils/index.d.ts.map +1 -0
  146. package/dist/mybodyshield/utils/navigation.d.ts +14 -0
  147. package/dist/mybodyshield/utils/navigation.d.ts.map +1 -0
  148. package/dist/mybodyshield/utils/pageContext.d.ts +28 -0
  149. package/dist/mybodyshield/utils/pageContext.d.ts.map +1 -0
  150. package/dist/mybodyshield/utils/pageContext.js +62 -0
  151. package/dist/mybodyshield/utils/pageContext.js.map +1 -0
  152. package/package.json +8 -2
@@ -1 +1 @@
1
- {"version":3,"file":"ProgressPage.js","names":[],"sources":["../../../../src/mybodyshield/pages/progress/ProgressPage.tsx"],"sourcesContent":["/**\n * Progress — body metrics over time.\n * @module mybodyshield/pages/progress\n *\n * A single-metric time-series view over the real `mybListBodyMetrics`\n * endpoint: pick a body-metric type (weight, body-fat, measurements…), see the\n * latest value + trend vs the previous reading, a token-styled line chart of the\n * history window, aggregate stats, and an offset-paginated log table. New\n * readings are recorded through `mybRecordBodyMetric`. No mock data — everything\n * derives from the hooks.\n */\n\nimport { useEffect, useMemo, useState } from 'react';\nimport {\n AreaChart,\n Area,\n XAxis,\n YAxis,\n CartesianGrid,\n Tooltip,\n ReferenceDot,\n ResponsiveContainer,\n} from 'recharts';\nimport { motion, useReducedMotion, type MotionProps } from 'framer-motion';\nimport { Plus, Activity, Sigma, ArrowDownUp } from 'lucide-react';\nimport {\n Button,\n Input,\n Label,\n Textarea,\n GlassCard,\n Select,\n SelectTrigger,\n SelectContent,\n SelectItem,\n SelectValue,\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n ResponsiveDialogDescription,\n ResponsiveDialogFooter,\n} from '@burdenoff/fe-libs/ui';\nimport {\n PageLayout,\n MetricsCard,\n FilterTabs,\n DataTable,\n Pagination,\n LoadingView,\n ErrorView,\n Section,\n EmptyState,\n EvidenceUpload,\n FileLink,\n type Column,\n type FilterOption,\n} from '../../components';\nimport { IconChip, TrendPill, METRIC_META } from '../_shared/visuals';\nimport {\n useMybBodyMetrics,\n useMybRecordBodyMetric,\n useOffsetPager,\n type UploadedMybFile,\n} from '../../hooks';\nimport type { MybBodyMetric, MybBodyMetricType, MybRecordMetricInput } from '../../types';\nimport {\n formatNumber,\n formatDate,\n formatDateTime,\n formatRelativeTime,\n formatEnumLabel,\n} from '../../utils/formatters';\n\n// Ordered enum surface for the metric-type selector (METRIC_META is a Record).\nconst METRIC_TYPES: MybBodyMetricType[] = [\n 'WEIGHT',\n 'BODY_FAT',\n 'RESTING_HEART_RATE',\n 'WAIST',\n 'CHEST',\n 'HIPS',\n 'ARM',\n 'THIGH',\n];\n\n// Which direction of change is \"good\" per metric (drives TrendPill colouring).\nconst GOOD_DIRECTION: Record<MybBodyMetricType, 'up' | 'down'> = {\n WEIGHT: 'down',\n BODY_FAT: 'down',\n RESTING_HEART_RATE: 'down',\n WAIST: 'down',\n HIPS: 'down',\n CHEST: 'up',\n ARM: 'up',\n THIGH: 'up',\n};\n\n// Window used for the chart / KPIs / aggregates (the log table paginates on top).\nconst CHART_WINDOW = 100;\nconst PAGE_SIZE = 25;\n\n// Brand emerald accent — the dominant color for the area chart, line & endpoint.\nconst CHART_ACCENT = 'var(--color-action-primary-bg)';\n\ninterface RecordForm {\n type: MybBodyMetricType;\n value: string;\n unit: string;\n measuredAt: string;\n notes: string;\n photo: UploadedMybFile | null;\n}\n\n/** A `Date` → `<input type=\"datetime-local\">` value (local wall-clock time). */\nconst toLocalInput = (d: Date): string => {\n const pad = (n: number) => String(n).padStart(2, '0');\n return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}T${pad(d.getHours())}:${pad(d.getMinutes())}`;\n};\n\nconst makeForm = (type: MybBodyMetricType): RecordForm => ({\n type,\n value: '',\n unit: METRIC_META[type].unit,\n measuredAt: toLocalInput(new Date()),\n notes: '',\n photo: null,\n});\n\nexport function ProgressPage() {\n const [selectedType, setSelectedType] = useState<MybBodyMetricType>('WEIGHT');\n const [open, setOpen] = useState(false);\n const [form, setForm] = useState<RecordForm>(() => makeForm('WEIGHT'));\n const [formError, setFormError] = useState<string | null>(null);\n\n const pager = useOffsetPager(PAGE_SIZE);\n\n // Reset the log table to page 1 whenever the metric type changes.\n // Depend on the stable `pager.reset` (NOT the whole pager, whose identity\n // changes with `page`) so advancing pages doesn't snap back to page 1.\n const resetPager = pager.reset;\n useEffect(() => {\n resetPager();\n }, [selectedType, resetPager]);\n\n // Wide window drives the chart, latest/trend KPIs and aggregate stats.\n const history = useMybBodyMetrics({\n type: selectedType,\n pagination: { limit: CHART_WINDOW },\n });\n // Independent offset-paginated slice powers the log table.\n const table = useMybBodyMetrics({\n type: selectedType,\n pagination: { limit: PAGE_SIZE, offset: pager.offset },\n });\n const record = useMybRecordBodyMetric();\n\n const reduced = useReducedMotion();\n const rise = (delay: number, hover = true): MotionProps =>\n reduced\n ? { initial: false }\n : {\n initial: { opacity: 0, y: 14 },\n animate: { opacity: 1, y: 0 },\n transition: { duration: 0.45, delay, ease: 'easeOut' },\n ...(hover\n ? {\n whileHover: {\n y: -3,\n transition: { type: 'spring', stiffness: 300, damping: 22 },\n },\n }\n : {}),\n };\n\n const meta = METRIC_META[selectedType];\n const MetricIcon = meta.icon;\n const goodDir = GOOD_DIRECTION[selectedType];\n\n const historyRows = useMemo<MybBodyMetric[]>(() => history.data?.items ?? [], [history.data]);\n\n // Ascending by measuredAt → chart series + robust latest/prior derivation.\n const ascending = useMemo(\n () =>\n [...historyRows].sort(\n (a, b) => new Date(a.measuredAt).getTime() - new Date(b.measuredAt).getTime()\n ),\n [historyRows]\n );\n\n const latest = ascending[ascending.length - 1];\n const prior = ascending[ascending.length - 2];\n const delta = latest && prior ? latest.value - prior.value : undefined;\n\n const chartData = useMemo(\n () => ascending.map((m) => ({ label: formatDate(m.measuredAt), value: m.value })),\n [ascending]\n );\n const lastPoint = chartData.length > 0 ? chartData[chartData.length - 1] : undefined;\n\n const stats = useMemo(() => {\n if (ascending.length === 0) return null;\n const values = ascending.map((m) => m.value);\n return {\n count: values.length,\n min: Math.min(...values),\n max: Math.max(...values),\n avg: values.reduce((a, b) => a + b, 0) / values.length,\n };\n }, [ascending]);\n\n const metricOptions: FilterOption<MybBodyMetricType>[] = METRIC_TYPES.map((t) => ({\n value: t,\n label: formatEnumLabel(t),\n }));\n\n const openRecord = () => {\n setForm(makeForm(selectedType));\n setFormError(null);\n setOpen(true);\n };\n\n const submit = async () => {\n const numValue = Number(form.value);\n if (form.value.trim() === '' || !Number.isFinite(numValue)) {\n setFormError('Enter a valid measurement value.');\n return;\n }\n setFormError(null);\n const input: MybRecordMetricInput = {\n type: form.type,\n value: numValue,\n unit: form.unit.trim() || METRIC_META[form.type].unit,\n measuredAt: form.measuredAt\n ? new Date(form.measuredAt).toISOString()\n : new Date().toISOString(),\n notes: form.notes.trim() ? form.notes.trim() : null,\n photoFileId: form.photo?.fileId ?? null,\n };\n try {\n await record.mutateAsync(input);\n setOpen(false);\n // Surface the just-recorded type so the new reading is visible immediately.\n setSelectedType(form.type);\n pager.reset();\n } catch (e) {\n setFormError(e instanceof Error ? e.message : 'Failed to record measurement.');\n }\n };\n\n const tableRows = table.data?.items ?? [];\n const tableColumns: Column<MybBodyMetric>[] = [\n {\n key: 'value',\n header: 'Value',\n align: 'right',\n sortValue: (row) => row.value,\n render: (row) => (\n <span className=\"font-medium tabular-nums text-text-primary\">\n {formatNumber(row.value)}\n <span className=\"ml-1 text-xs font-normal text-text-tertiary\">{row.unit}</span>\n </span>\n ),\n },\n {\n key: 'measuredAt',\n header: 'Measured',\n sortValue: (row) => new Date(row.measuredAt).getTime(),\n render: (row) => (\n <span className=\"whitespace-nowrap text-text-secondary\">\n {formatDateTime(row.measuredAt)}\n </span>\n ),\n },\n {\n key: 'notes',\n header: 'Notes',\n hideOnMobile: true,\n render: (row) =>\n row.notes ? (\n <span className=\"text-text-secondary\">{row.notes}</span>\n ) : (\n <span className=\"text-text-tertiary\">—</span>\n ),\n },\n {\n key: 'photo',\n header: 'Photo',\n render: (row) =>\n row.photo?.id ? (\n <FileLink fileId={row.photo.id} />\n ) : (\n <span className=\"text-text-tertiary\">—</span>\n ),\n },\n ];\n\n const recordButton = (\n <Button onClick={openRecord}>\n <Plus className=\"mr-1.5 h-4 w-4\" /> Record measurement\n </Button>\n );\n\n return (\n <PageLayout\n title=\"Progress\"\n description=\"Body metrics over time — weight, composition & measurements.\"\n actions={recordButton}\n >\n {/* Metric-type selector */}\n <div className=\"mb-6 overflow-x-auto\">\n <FilterTabs options={metricOptions} value={selectedType} onChange={setSelectedType} />\n </div>\n\n {history.isLoading && historyRows.length === 0 ? (\n <LoadingView label={`Loading ${formatEnumLabel(selectedType).toLowerCase()} history…`} />\n ) : history.isError ? (\n <ErrorView\n message=\"We couldn't load your body-metric history.\"\n onRetry={() => history.refetch()}\n />\n ) : historyRows.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n icon={<MetricIcon className=\"h-10 w-10\" />}\n title={`No ${formatEnumLabel(selectedType).toLowerCase()} readings yet`}\n description=\"Record a measurement to start tracking this metric over time.\"\n action={\n <Button onClick={openRecord}>\n <Plus className=\"mr-1.5 h-4 w-4\" /> Record measurement\n </Button>\n }\n />\n ) : (\n <div className=\"space-y-6\">\n {/* Latest + trend + chart */}\n <motion.div {...rise(0)}>\n <GlassCard\n treatment=\"glass\"\n className=\"rounded-2xl p-6 transition-shadow duration-300 hover:shadow-2xl\"\n >\n <div className=\"flex flex-wrap items-start justify-between gap-4\">\n <div className=\"flex items-center gap-3\">\n <IconChip icon={<MetricIcon className=\"h-5 w-5\" />} tint={meta.tint} />\n <div>\n <h2 className=\"text-base font-semibold text-text-primary\">\n {formatEnumLabel(selectedType)}\n </h2>\n <p className=\"text-sm text-text-secondary\">\n {latest\n ? `Last measured ${formatRelativeTime(latest.measuredAt)}`\n : 'No measurements yet'}\n </p>\n </div>\n </div>\n <div className=\"text-right\">\n <p className=\"text-3xl font-bold leading-none tracking-tight tabular-nums text-text-primary sm:text-4xl\">\n {latest ? formatNumber(latest.value) : '—'}\n {latest && (\n <span className=\"ml-1 text-base font-medium text-text-tertiary\">\n {meta.unit}\n </span>\n )}\n </p>\n {delta != null && (\n <div className=\"mt-2 flex justify-end\">\n <span className=\"inline-flex items-center rounded-full bg-bg-sunken px-2.5 py-1 ring-1 ring-border-seam\">\n <TrendPill delta={delta} unit={` ${meta.unit}`} goodDirection={goodDir} />\n </span>\n </div>\n )}\n </div>\n </div>\n\n <div className=\"mt-6 h-64 w-full\">\n <ResponsiveContainer width=\"100%\" height=\"100%\">\n <AreaChart data={chartData} margin={{ top: 8, right: 14, bottom: 4, left: -8 }}>\n <defs>\n <linearGradient id=\"myb-progress-fill\" x1=\"0\" y1=\"0\" x2=\"0\" y2=\"1\">\n <stop offset=\"0%\" stopColor={CHART_ACCENT} stopOpacity={0.38} />\n <stop offset=\"55%\" stopColor={CHART_ACCENT} stopOpacity={0.12} />\n <stop offset=\"100%\" stopColor={CHART_ACCENT} stopOpacity={0} />\n </linearGradient>\n </defs>\n <CartesianGrid\n stroke=\"var(--color-border-seam)\"\n strokeDasharray=\"4 4\"\n vertical={false}\n />\n <XAxis\n dataKey=\"label\"\n stroke=\"var(--color-border-seam)\"\n tickLine={false}\n axisLine={false}\n tick={{ fill: 'var(--color-text-tertiary)', fontSize: 12 }}\n tickMargin={8}\n minTickGap={24}\n />\n <YAxis\n stroke=\"var(--color-border-seam)\"\n tickLine={false}\n axisLine={false}\n tick={{ fill: 'var(--color-text-tertiary)', fontSize: 12 }}\n width={44}\n domain={['auto', 'auto']}\n />\n <Tooltip\n cursor={{ stroke: 'var(--color-border-seam)', strokeDasharray: '4 4' }}\n contentStyle={{\n background: 'var(--color-bg-surface)',\n border: '1px solid var(--color-border-seam)',\n borderRadius: 12,\n color: 'var(--color-text-primary)',\n }}\n labelStyle={{ color: 'var(--color-text-tertiary)' }}\n itemStyle={{ color: 'var(--color-text-primary)' }}\n formatter={(value) => [\n `${formatNumber(Number(value))} ${meta.unit}`,\n formatEnumLabel(selectedType),\n ]}\n />\n <Area\n type=\"monotone\"\n dataKey=\"value\"\n name={formatEnumLabel(selectedType)}\n stroke={CHART_ACCENT}\n strokeWidth={2.5}\n fill=\"url(#myb-progress-fill)\"\n fillOpacity={1}\n dot={false}\n activeDot={{\n r: 5,\n strokeWidth: 2,\n stroke: 'var(--color-bg-surface)',\n fill: CHART_ACCENT,\n }}\n isAnimationActive={!reduced}\n animationDuration={900}\n />\n {lastPoint && (\n <ReferenceDot\n x={lastPoint.label}\n y={lastPoint.value}\n r={12}\n isFront\n ifOverflow=\"visible\"\n fill={CHART_ACCENT}\n fillOpacity={0.16}\n stroke=\"none\"\n />\n )}\n {lastPoint && (\n <ReferenceDot\n x={lastPoint.label}\n y={lastPoint.value}\n r={5}\n isFront\n ifOverflow=\"visible\"\n fill={CHART_ACCENT}\n stroke=\"var(--color-bg-surface)\"\n strokeWidth={2}\n />\n )}\n </AreaChart>\n </ResponsiveContainer>\n </div>\n </GlassCard>\n </motion.div>\n\n {/* Aggregate stats over the loaded window (not full history) */}\n {stats && (\n <div>\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-3\">\n <motion.div {...rise(0.08)}>\n <MetricsCard\n className=\"h-full rounded-2xl transition-shadow duration-300 hover:shadow-xl\"\n icon={<IconChip icon={<Activity className=\"h-5 w-5\" />} tint={CHART_ACCENT} />}\n value={formatNumber(stats.count)}\n label=\"Recent readings\"\n />\n </motion.div>\n <motion.div {...rise(0.14)}>\n <MetricsCard\n className=\"h-full rounded-2xl transition-shadow duration-300 hover:shadow-xl\"\n icon={<IconChip icon={<Sigma className=\"h-5 w-5\" />} tint={CHART_ACCENT} />}\n value={`${formatNumber(stats.avg, { maximumFractionDigits: 1 })} ${meta.unit}`}\n label={`Window average ${formatEnumLabel(selectedType).toLowerCase()}`}\n />\n </motion.div>\n <motion.div {...rise(0.2)}>\n <MetricsCard\n className=\"h-full rounded-2xl transition-shadow duration-300 hover:shadow-xl\"\n icon={\n <IconChip icon={<ArrowDownUp className=\"h-5 w-5\" />} tint={CHART_ACCENT} />\n }\n value={`${formatNumber(stats.min)}–${formatNumber(stats.max)}`}\n label={`Window range (${meta.unit})`}\n />\n </motion.div>\n </div>\n <p className=\"mt-2 text-xs text-text-tertiary\">\n Based on your most recent {formatNumber(stats.count)}{' '}\n {formatEnumLabel(selectedType).toLowerCase()} reading\n {stats.count === 1 ? '' : 's'} (up to {formatNumber(CHART_WINDOW)}) — not a\n full-history total.\n </p>\n </div>\n )}\n\n {/* Full measurement log */}\n <motion.div {...rise(0.26, false)}>\n <Section title=\"Measurement log\" description=\"Every recorded reading for this metric.\">\n {table.isError ? (\n <ErrorView\n message=\"We couldn't load this page of measurements.\"\n onRetry={() => table.refetch()}\n />\n ) : table.isLoading && tableRows.length === 0 ? (\n <div className=\"py-8 text-center text-sm text-text-secondary\">\n Loading measurements…\n </div>\n ) : tableRows.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n icon={<MetricIcon className=\"h-10 w-10\" />}\n title=\"No measurements on this page\"\n description=\"Go back a page or record a new measurement.\"\n />\n ) : (\n <>\n <DataTable\n columns={tableColumns}\n rows={tableRows}\n rowKey={(row) => row.id}\n defaultSort={{ key: 'measuredAt', dir: 'desc' }}\n />\n <Pagination\n page={pager.page}\n pageSize={PAGE_SIZE}\n itemCount={tableRows.length}\n hasMore={table.data?.pageInfo?.hasMore ?? false}\n onPageChange={(t) => pager.onPageChange(t)}\n />\n </>\n )}\n </Section>\n </motion.div>\n </div>\n )}\n\n {/* Record-measurement dialog */}\n <ResponsiveDialog open={open} onOpenChange={setOpen}>\n <ResponsiveDialogContent>\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>Record measurement</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n Log a new body-metric reading to track your progress over time.\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n\n <div className=\"space-y-4 py-2\">\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-metric-type\">Metric</Label>\n <Select\n value={form.type}\n onValueChange={(v) => {\n // `v` is constrained to METRIC_TYPES by the SelectItems below.\n const nextType = v as MybBodyMetricType;\n setForm((prev) => ({\n ...prev,\n type: nextType,\n unit: METRIC_META[nextType].unit,\n }));\n }}\n >\n <SelectTrigger id=\"myb-metric-type\">\n <SelectValue placeholder=\"Select a metric\" />\n </SelectTrigger>\n <SelectContent>\n {METRIC_TYPES.map((t) => (\n <SelectItem key={t} value={t}>\n {formatEnumLabel(t)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n\n <div className=\"grid grid-cols-2 gap-3\">\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-metric-value\">Value</Label>\n <Input\n id=\"myb-metric-value\"\n type=\"number\"\n inputMode=\"decimal\"\n step=\"any\"\n value={form.value}\n onChange={(e) => setForm((prev) => ({ ...prev, value: e.target.value }))}\n placeholder=\"0\"\n />\n </div>\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-metric-unit\">Unit</Label>\n <Input\n id=\"myb-metric-unit\"\n value={form.unit}\n onChange={(e) => setForm((prev) => ({ ...prev, unit: e.target.value }))}\n placeholder={METRIC_META[form.type].unit}\n />\n </div>\n </div>\n\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-metric-measured\">Measured at</Label>\n <Input\n id=\"myb-metric-measured\"\n type=\"datetime-local\"\n value={form.measuredAt}\n onChange={(e) => setForm((prev) => ({ ...prev, measuredAt: e.target.value }))}\n />\n </div>\n\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-metric-notes\">Notes</Label>\n <Textarea\n id=\"myb-metric-notes\"\n value={form.notes}\n onChange={(e) => setForm((prev) => ({ ...prev, notes: e.target.value }))}\n placeholder=\"Optional context (fasted, post-workout, morning…)\"\n rows={3}\n />\n </div>\n\n {/* Optional progress photo — uploads via useMybFileUpload (byte-progress\n + inline error handled inside EvidenceUpload) and sets photoFileId. */}\n <EvidenceUpload\n id=\"myb-metric-photo\"\n label=\"Progress photo (optional)\"\n value={form.photo}\n onChange={(photo) => setForm((prev) => ({ ...prev, photo }))}\n accept=\"image/*\"\n disabled={record.isPending}\n hint=\"Attach a progress photo for this reading.\"\n />\n\n {formError && <p className=\"text-sm text-status-error-text\">{formError}</p>}\n </div>\n\n <ResponsiveDialogFooter>\n <Button variant=\"outline\" onClick={() => setOpen(false)} disabled={record.isPending}>\n Cancel\n </Button>\n <Button onClick={submit} disabled={record.isPending}>\n {record.isPending ? 'Recording…' : 'Record'}\n </Button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"ProgressPage.js","names":[],"sources":["../../../../src/mybodyshield/pages/progress/ProgressPage.tsx"],"sourcesContent":["/**\n * Progress — body metrics over time.\n * @module mybodyshield/pages/progress\n *\n * A single-metric time-series view over the real `mybListBodyMetrics`\n * endpoint: pick a body-metric type (weight, body-fat, measurements…), see the\n * latest value + trend vs the previous reading, a token-styled line chart of the\n * history window, aggregate stats, and an offset-paginated log table. New\n * readings are recorded through `mybRecordBodyMetric`. No mock data — everything\n * derives from the hooks.\n */\n\nimport { useEffect, useMemo, useState } from 'react';\nimport {\n AreaChart,\n Area,\n XAxis,\n YAxis,\n CartesianGrid,\n Tooltip,\n ReferenceDot,\n ResponsiveContainer,\n} from 'recharts';\nimport { motion, useReducedMotion, type MotionProps } from 'framer-motion';\nimport { Plus, Activity, Sigma, ArrowDownUp } from 'lucide-react';\nimport {\n Button,\n Input,\n Label,\n Textarea,\n GlassCard,\n Select,\n SelectTrigger,\n SelectContent,\n SelectItem,\n SelectValue,\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n ResponsiveDialogDescription,\n ResponsiveDialogFooter,\n} from '@burdenoff/fe-libs/ui';\nimport {\n PageLayout,\n MetricsCard,\n FilterTabs,\n DataTable,\n Pagination,\n LoadingView,\n ErrorView,\n Section,\n EmptyState,\n EvidenceUpload,\n FileLink,\n type Column,\n type FilterOption,\n} from '../../components';\nimport { IconChip, TrendPill, METRIC_META } from '../_shared/visuals';\nimport {\n useMybBodyMetrics,\n useMybRecordBodyMetric,\n useOffsetPager,\n type UploadedMybFile,\n} from '../../hooks';\nimport type { MybBodyMetric, MybBodyMetricType, MybRecordMetricInput } from '../../types';\nimport {\n formatNumber,\n formatDate,\n formatDateTime,\n formatRelativeTime,\n formatEnumLabel,\n} from '../../utils/formatters';\n\n// Ordered enum surface for the metric-type selector (METRIC_META is a Record).\nconst METRIC_TYPES: MybBodyMetricType[] = [\n 'WEIGHT',\n 'BODY_FAT',\n 'RESTING_HEART_RATE',\n 'WAIST',\n 'CHEST',\n 'HIPS',\n 'ARM',\n 'THIGH',\n];\n\n// Which direction of change is \"good\" per metric (drives TrendPill colouring).\nconst GOOD_DIRECTION: Record<MybBodyMetricType, 'up' | 'down'> = {\n WEIGHT: 'down',\n BODY_FAT: 'down',\n RESTING_HEART_RATE: 'down',\n WAIST: 'down',\n HIPS: 'down',\n CHEST: 'up',\n ARM: 'up',\n THIGH: 'up',\n};\n\n// Window used for the chart / KPIs / aggregates (the log table paginates on top).\nconst CHART_WINDOW = 100;\nconst PAGE_SIZE = 25;\n\n// Brand emerald accent — the dominant color for the area chart, line & endpoint.\nconst CHART_ACCENT = 'var(--color-action-primary-bg)';\n\ninterface RecordForm {\n type: MybBodyMetricType;\n value: string;\n unit: string;\n measuredAt: string;\n notes: string;\n photo: UploadedMybFile | null;\n}\n\n/** A `Date` → `<input type=\"datetime-local\">` value (local wall-clock time). */\nconst toLocalInput = (d: Date): string => {\n const pad = (n: number) => String(n).padStart(2, '0');\n return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}T${pad(d.getHours())}:${pad(d.getMinutes())}`;\n};\n\nconst makeForm = (type: MybBodyMetricType): RecordForm => ({\n type,\n value: '',\n unit: METRIC_META[type].unit,\n measuredAt: toLocalInput(new Date()),\n notes: '',\n photo: null,\n});\n\nexport function ProgressPage() {\n const [selectedType, setSelectedType] = useState<MybBodyMetricType>('WEIGHT');\n const [open, setOpen] = useState(false);\n const [form, setForm] = useState<RecordForm>(() => makeForm('WEIGHT'));\n const [formError, setFormError] = useState<string | null>(null);\n\n const pager = useOffsetPager(PAGE_SIZE);\n\n // Reset the log table to page 1 whenever the metric type changes.\n // Depend on the stable `pager.reset` (NOT the whole pager, whose identity\n // changes with `page`) so advancing pages doesn't snap back to page 1.\n const resetPager = pager.reset;\n useEffect(() => {\n resetPager();\n }, [selectedType, resetPager]);\n\n // Wide window drives the chart, latest/trend KPIs and aggregate stats.\n const history = useMybBodyMetrics({\n type: selectedType,\n pagination: { limit: CHART_WINDOW },\n });\n // Independent offset-paginated slice powers the log table.\n const table = useMybBodyMetrics({\n type: selectedType,\n pagination: { limit: PAGE_SIZE, offset: pager.offset },\n });\n const record = useMybRecordBodyMetric();\n\n const reduced = useReducedMotion();\n const rise = (delay: number, hover = true): MotionProps =>\n reduced\n ? { initial: false }\n : {\n initial: { opacity: 0, y: 14 },\n animate: { opacity: 1, y: 0 },\n transition: { duration: 0.45, delay, ease: 'easeOut' },\n ...(hover\n ? {\n whileHover: {\n y: -3,\n transition: { type: 'spring', stiffness: 300, damping: 22 },\n },\n }\n : {}),\n };\n\n const meta = METRIC_META[selectedType];\n const MetricIcon = meta.icon;\n const goodDir = GOOD_DIRECTION[selectedType];\n\n const historyRows = useMemo<MybBodyMetric[]>(() => history.data?.items ?? [], [history.data]);\n\n // Ascending by measuredAt → chart series + robust latest/prior derivation.\n const ascending = useMemo(\n () =>\n [...historyRows].sort(\n (a, b) => new Date(a.measuredAt).getTime() - new Date(b.measuredAt).getTime()\n ),\n [historyRows]\n );\n\n const latest = ascending[ascending.length - 1];\n const prior = ascending[ascending.length - 2];\n const delta = latest && prior ? latest.value - prior.value : undefined;\n\n const chartData = useMemo(\n () => ascending.map((m) => ({ label: formatDate(m.measuredAt), value: m.value })),\n [ascending]\n );\n const lastPoint = chartData.length > 0 ? chartData[chartData.length - 1] : undefined;\n\n const stats = useMemo(() => {\n if (ascending.length === 0) return null;\n const values = ascending.map((m) => m.value);\n return {\n count: values.length,\n min: Math.min(...values),\n max: Math.max(...values),\n avg: values.reduce((a, b) => a + b, 0) / values.length,\n };\n }, [ascending]);\n\n const metricOptions: FilterOption<MybBodyMetricType>[] = METRIC_TYPES.map((t) => ({\n value: t,\n label: formatEnumLabel(t),\n }));\n\n const openRecord = () => {\n setForm(makeForm(selectedType));\n setFormError(null);\n setOpen(true);\n };\n\n const submit = async () => {\n const numValue = Number(form.value);\n if (form.value.trim() === '' || !Number.isFinite(numValue)) {\n setFormError('Enter a valid measurement value.');\n return;\n }\n setFormError(null);\n const input: MybRecordMetricInput = {\n type: form.type,\n value: numValue,\n unit: form.unit.trim() || METRIC_META[form.type].unit,\n measuredAt: form.measuredAt\n ? new Date(form.measuredAt).toISOString()\n : new Date().toISOString(),\n notes: form.notes.trim() ? form.notes.trim() : null,\n photoFileId: form.photo?.fileId ?? null,\n };\n try {\n await record.mutateAsync(input);\n setOpen(false);\n // Surface the just-recorded type so the new reading is visible immediately.\n setSelectedType(form.type);\n pager.reset();\n } catch (e) {\n setFormError(e instanceof Error ? e.message : 'Failed to record measurement.');\n }\n };\n\n const tableRows = table.data?.items ?? [];\n const tableColumns: Column<MybBodyMetric>[] = [\n {\n key: 'value',\n header: 'Value',\n align: 'right',\n sortValue: (row) => row.value,\n render: (row) => (\n <span className=\"font-medium tabular-nums text-text-primary\">\n {formatNumber(row.value)}\n <span className=\"ml-1 text-xs font-normal text-text-tertiary\">{row.unit}</span>\n </span>\n ),\n },\n {\n key: 'measuredAt',\n header: 'Measured',\n sortValue: (row) => new Date(row.measuredAt).getTime(),\n render: (row) => (\n <span className=\"whitespace-nowrap text-text-secondary\">\n {formatDateTime(row.measuredAt)}\n </span>\n ),\n },\n {\n key: 'notes',\n header: 'Notes',\n hideOnMobile: true,\n render: (row) =>\n row.notes ? (\n <span className=\"text-text-secondary\">{row.notes}</span>\n ) : (\n <span className=\"text-text-tertiary\">—</span>\n ),\n },\n {\n key: 'photo',\n header: 'Photo',\n render: (row) =>\n row.photo?.id ? (\n <FileLink fileId={row.photo.id} />\n ) : (\n <span className=\"text-text-tertiary\">—</span>\n ),\n },\n ];\n\n const recordButton = (\n <Button onClick={openRecord}>\n <Plus className=\"mr-1.5 h-4 w-4\" /> Record measurement\n </Button>\n );\n\n return (\n <PageLayout\n title=\"Progress\"\n description=\"Body metrics over time — weight, composition & measurements.\"\n actions={recordButton}\n >\n {/* Metric-type selector */}\n <div className=\"mb-6 overflow-x-auto\">\n <FilterTabs options={metricOptions} value={selectedType} onChange={setSelectedType} />\n </div>\n\n {history.isLoading && historyRows.length === 0 ? (\n <LoadingView label={`Loading ${formatEnumLabel(selectedType).toLowerCase()} history…`} />\n ) : history.isError ? (\n <ErrorView\n message=\"We couldn't load your body-metric history.\"\n onRetry={() => history.refetch()}\n />\n ) : historyRows.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n icon={<MetricIcon className=\"h-10 w-10\" />}\n title={`No ${formatEnumLabel(selectedType).toLowerCase()} readings yet`}\n description=\"Record a measurement to start tracking this metric over time.\"\n action={\n <Button onClick={openRecord}>\n <Plus className=\"mr-1.5 h-4 w-4\" /> Record measurement\n </Button>\n }\n />\n ) : (\n <div className=\"space-y-6\">\n {/* Latest + trend + chart */}\n <motion.div {...rise(0)}>\n <GlassCard\n treatment=\"glass\"\n className=\"rounded-2xl p-6 transition-shadow duration-300 hover:shadow-2xl\"\n >\n <div className=\"flex flex-wrap items-start justify-between gap-4\">\n <div className=\"flex items-center gap-3\">\n <IconChip icon={<MetricIcon className=\"h-5 w-5\" />} tint={meta.tint} />\n <div>\n <h2 className=\"text-base font-semibold text-text-primary\">\n {formatEnumLabel(selectedType)}\n </h2>\n <p className=\"text-sm text-text-secondary\">\n {latest\n ? `Last measured ${formatRelativeTime(latest.measuredAt)}`\n : 'No measurements yet'}\n </p>\n </div>\n </div>\n <div className=\"text-right\">\n <p className=\"text-3xl font-bold leading-none tracking-tight tabular-nums text-text-primary sm:text-4xl\">\n {latest ? formatNumber(latest.value) : '—'}\n {latest && (\n <span className=\"ml-1 text-base font-medium text-text-tertiary\">\n {meta.unit}\n </span>\n )}\n </p>\n {delta != null && (\n <div className=\"mt-2 flex justify-end\">\n <span className=\"inline-flex items-center rounded-full bg-bg-sunken px-2.5 py-1 ring-1 ring-border-seam\">\n <TrendPill delta={delta} unit={` ${meta.unit}`} goodDirection={goodDir} />\n </span>\n </div>\n )}\n </div>\n </div>\n\n <div className=\"mt-6 h-64 w-full\">\n <ResponsiveContainer width=\"100%\" height=\"100%\">\n <AreaChart data={chartData} margin={{ top: 8, right: 14, bottom: 4, left: -8 }}>\n <defs>\n <linearGradient id=\"myb-progress-fill\" x1=\"0\" y1=\"0\" x2=\"0\" y2=\"1\">\n <stop offset=\"0%\" stopColor={CHART_ACCENT} stopOpacity={0.38} />\n <stop offset=\"55%\" stopColor={CHART_ACCENT} stopOpacity={0.12} />\n <stop offset=\"100%\" stopColor={CHART_ACCENT} stopOpacity={0} />\n </linearGradient>\n </defs>\n <CartesianGrid\n stroke=\"var(--color-border-seam)\"\n strokeDasharray=\"4 4\"\n vertical={false}\n />\n <XAxis\n dataKey=\"label\"\n stroke=\"var(--color-border-seam)\"\n tickLine={false}\n axisLine={false}\n tick={{ fill: 'var(--color-text-tertiary)', fontSize: 12 }}\n tickMargin={8}\n minTickGap={24}\n />\n <YAxis\n stroke=\"var(--color-border-seam)\"\n tickLine={false}\n axisLine={false}\n tick={{ fill: 'var(--color-text-tertiary)', fontSize: 12 }}\n width={44}\n domain={['auto', 'auto']}\n />\n <Tooltip\n cursor={{ stroke: 'var(--color-border-seam)', strokeDasharray: '4 4' }}\n contentStyle={{\n background: 'var(--color-bg-surface)',\n border: '1px solid var(--color-border-seam)',\n borderRadius: 12,\n color: 'var(--color-text-primary)',\n }}\n labelStyle={{ color: 'var(--color-text-tertiary)' }}\n itemStyle={{ color: 'var(--color-text-primary)' }}\n formatter={(value) => [\n `${formatNumber(Number(value))} ${meta.unit}`,\n formatEnumLabel(selectedType),\n ]}\n />\n <Area\n type=\"monotone\"\n dataKey=\"value\"\n name={formatEnumLabel(selectedType)}\n stroke={CHART_ACCENT}\n strokeWidth={2.5}\n fill=\"url(#myb-progress-fill)\"\n fillOpacity={1}\n dot={false}\n activeDot={{\n r: 5,\n strokeWidth: 2,\n stroke: 'var(--color-bg-surface)',\n fill: CHART_ACCENT,\n }}\n isAnimationActive={!reduced}\n animationDuration={900}\n />\n {lastPoint && (\n <ReferenceDot\n x={lastPoint.label}\n y={lastPoint.value}\n r={12}\n isFront\n ifOverflow=\"visible\"\n fill={CHART_ACCENT}\n fillOpacity={0.16}\n stroke=\"none\"\n />\n )}\n {lastPoint && (\n <ReferenceDot\n x={lastPoint.label}\n y={lastPoint.value}\n r={5}\n isFront\n ifOverflow=\"visible\"\n fill={CHART_ACCENT}\n stroke=\"var(--color-bg-surface)\"\n strokeWidth={2}\n />\n )}\n </AreaChart>\n </ResponsiveContainer>\n </div>\n </GlassCard>\n </motion.div>\n\n {/* Aggregate stats over the loaded window (not full history) */}\n {stats && (\n <div>\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-3\">\n <motion.div {...rise(0.08)}>\n <MetricsCard\n className=\"h-full rounded-2xl transition-shadow duration-300 hover:shadow-xl\"\n icon={<IconChip icon={<Activity className=\"h-5 w-5\" />} tint={CHART_ACCENT} />}\n value={formatNumber(stats.count)}\n label=\"Recent readings\"\n />\n </motion.div>\n <motion.div {...rise(0.14)}>\n <MetricsCard\n className=\"h-full rounded-2xl transition-shadow duration-300 hover:shadow-xl\"\n icon={<IconChip icon={<Sigma className=\"h-5 w-5\" />} tint={CHART_ACCENT} />}\n value={`${formatNumber(stats.avg, { maximumFractionDigits: 1 })} ${meta.unit}`}\n label={`Window average ${formatEnumLabel(selectedType).toLowerCase()}`}\n />\n </motion.div>\n <motion.div {...rise(0.2)}>\n <MetricsCard\n className=\"h-full rounded-2xl transition-shadow duration-300 hover:shadow-xl\"\n icon={\n <IconChip icon={<ArrowDownUp className=\"h-5 w-5\" />} tint={CHART_ACCENT} />\n }\n value={`${formatNumber(stats.min)}–${formatNumber(stats.max)}`}\n label={`Window range (${meta.unit})`}\n />\n </motion.div>\n </div>\n <p className=\"mt-2 text-xs text-text-tertiary\">\n Based on your most recent {formatNumber(stats.count)}{' '}\n {formatEnumLabel(selectedType).toLowerCase()} reading\n {stats.count === 1 ? '' : 's'} (up to {formatNumber(CHART_WINDOW)}) — not a\n full-history total.\n </p>\n </div>\n )}\n\n {/* Full measurement log */}\n <motion.div {...rise(0.26, false)}>\n <Section title=\"Measurement log\" description=\"Every recorded reading for this metric.\">\n {table.isError ? (\n <ErrorView\n message=\"We couldn't load this page of measurements.\"\n onRetry={() => table.refetch()}\n />\n ) : table.isLoading && tableRows.length === 0 ? (\n <div className=\"py-8 text-center text-sm text-text-secondary\">\n Loading measurements…\n </div>\n ) : tableRows.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n icon={<MetricIcon className=\"h-10 w-10\" />}\n title=\"No measurements on this page\"\n description=\"Go back a page or record a new measurement.\"\n />\n ) : (\n <>\n <DataTable\n columns={tableColumns}\n rows={tableRows}\n rowKey={(row) => row.id}\n defaultSort={{ key: 'measuredAt', dir: 'desc' }}\n />\n <Pagination\n page={pager.page}\n pageSize={PAGE_SIZE}\n itemCount={tableRows.length}\n hasMore={table.data?.pageInfo?.hasMore ?? false}\n onPageChange={(t) => pager.onPageChange(t)}\n />\n </>\n )}\n </Section>\n </motion.div>\n </div>\n )}\n\n {/* Record-measurement dialog */}\n <ResponsiveDialog open={open} onOpenChange={setOpen}>\n <ResponsiveDialogContent>\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>Record measurement</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n Log a new body-metric reading to track your progress over time.\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n\n <div className=\"space-y-4 py-2\">\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-metric-type\">Metric</Label>\n <Select\n value={form.type}\n onValueChange={(v) => {\n // `v` is constrained to METRIC_TYPES by the SelectItems below.\n const nextType = v as MybBodyMetricType;\n setForm((prev) => ({\n ...prev,\n type: nextType,\n unit: METRIC_META[nextType].unit,\n }));\n }}\n >\n <SelectTrigger id=\"myb-metric-type\">\n <SelectValue placeholder=\"Select a metric\" />\n </SelectTrigger>\n <SelectContent>\n {METRIC_TYPES.map((t) => (\n <SelectItem key={t} value={t}>\n {formatEnumLabel(t)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n\n <div className=\"grid grid-cols-2 gap-3\">\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-metric-value\">Value</Label>\n <Input\n id=\"myb-metric-value\"\n type=\"number\"\n inputMode=\"decimal\"\n step=\"any\"\n value={form.value}\n onChange={(e) => setForm((prev) => ({ ...prev, value: e.target.value }))}\n placeholder=\"0\"\n />\n </div>\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-metric-unit\">Unit</Label>\n <Input\n id=\"myb-metric-unit\"\n value={form.unit}\n onChange={(e) => setForm((prev) => ({ ...prev, unit: e.target.value }))}\n placeholder={METRIC_META[form.type].unit}\n />\n </div>\n </div>\n\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-metric-measured\">Measured at</Label>\n <Input\n id=\"myb-metric-measured\"\n type=\"datetime-local\"\n value={form.measuredAt}\n onChange={(e) => setForm((prev) => ({ ...prev, measuredAt: e.target.value }))}\n />\n </div>\n\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-metric-notes\">Notes</Label>\n <Textarea\n id=\"myb-metric-notes\"\n value={form.notes}\n onChange={(e) => setForm((prev) => ({ ...prev, notes: e.target.value }))}\n placeholder=\"Optional context (fasted, post-workout, morning…)\"\n rows={3}\n />\n </div>\n\n {/* Optional progress photo — uploads via useMybFileUpload (byte-progress\n + inline error handled inside EvidenceUpload) and sets photoFileId. */}\n <EvidenceUpload\n id=\"myb-metric-photo\"\n label=\"Progress photo (optional)\"\n value={form.photo}\n onChange={(photo) => setForm((prev) => ({ ...prev, photo }))}\n accept=\"image/*\"\n disabled={record.isPending}\n hint=\"Attach a progress photo for this reading.\"\n />\n\n {formError && <p className=\"text-sm text-status-error-text\">{formError}</p>}\n </div>\n\n <ResponsiveDialogFooter>\n <Button variant=\"outline\" onClick={() => setOpen(false)} disabled={record.isPending}>\n Cancel\n </Button>\n <Button onClick={submit} disabled={record.isPending}>\n {record.isPending ? 'Recording…' : 'Record'}\n </Button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n </PageLayout>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA2EA,IAAM,IAAoC;CACxC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD,EAGK,KAA2D;CAC/D,QAAQ;CACR,UAAU;CACV,oBAAoB;CACpB,OAAO;CACP,MAAM;CACN,OAAO;CACP,KAAK;CACL,OAAO;CACR,EAGK,IAAe,KACf,IAAY,IAGZ,IAAe,kCAYf,KAAgB,MAAoB;CACxC,IAAM,KAAO,MAAc,OAAO,EAAE,CAAC,SAAS,GAAG,IAAI;AACrD,QAAO,GAAG,EAAE,aAAa,CAAC,GAAG,EAAI,EAAE,UAAU,GAAG,EAAE,CAAC,GAAG,EAAI,EAAE,SAAS,CAAC,CAAC,GAAG,EAAI,EAAE,UAAU,CAAC,CAAC,GAAG,EAAI,EAAE,YAAY,CAAC;GAG9G,KAAY,OAAyC;CACzD;CACA,OAAO;CACP,MAAM,EAAY,GAAM;CACxB,YAAY,kBAAa,IAAI,MAAM,CAAC;CACpC,OAAO;CACP,OAAO;CACR;AAED,SAAgB,IAAe;CAC7B,IAAM,CAAC,GAAc,KAAmB,EAA4B,SAAS,EACvE,CAAC,IAAM,KAAW,EAAS,GAAM,EACjC,CAAC,GAAM,KAAW,QAA2B,EAAS,SAAS,CAAC,EAChE,CAAC,GAAW,KAAgB,EAAwB,KAAK,EAEzD,IAAQ,GAAe,EAAU,EAKjC,IAAa,EAAM;AACzB,UAAgB;AACd,KAAY;IACX,CAAC,GAAc,EAAW,CAAC;CAG9B,IAAM,IAAU,EAAkB;EAChC,MAAM;EACN,YAAY,EAAE,OAAO,GAAc;EACpC,CAAC,EAEI,IAAQ,EAAkB;EAC9B,MAAM;EACN,YAAY;GAAE,OAAO;GAAW,QAAQ,EAAM;GAAQ;EACvD,CAAC,EACI,IAAS,IAAwB,EAEjC,IAAU,IAAkB,EAC5B,KAAQ,GAAe,IAAQ,OACnC,IACI,EAAE,SAAS,IAAO,GAClB;EACE,SAAS;GAAE,SAAS;GAAG,GAAG;GAAI;EAC9B,SAAS;GAAE,SAAS;GAAG,GAAG;GAAG;EAC7B,YAAY;GAAE,UAAU;GAAM;GAAO,MAAM;GAAW;EACtD,GAAI,IACA,EACE,YAAY;GACV,GAAG;GACH,YAAY;IAAE,MAAM;IAAU,WAAW;IAAK,SAAS;IAAI;GAC5D,EACF,GACD,EAAE;EACP,EAED,IAAO,EAAY,IACnB,IAAa,EAAK,MAClB,KAAU,GAAe,IAEzB,IAAc,QAA+B,EAAQ,MAAM,SAAS,EAAE,EAAE,CAAC,EAAQ,KAAK,CAAC,EAGvF,IAAY,QAEd,CAAC,GAAG,EAAY,CAAC,MACd,GAAG,MAAM,IAAI,KAAK,EAAE,WAAW,CAAC,SAAS,GAAG,IAAI,KAAK,EAAE,WAAW,CAAC,SAAS,CAC9E,EACH,CAAC,EAAY,CACd,EAEK,IAAS,EAAU,EAAU,SAAS,IACtC,IAAQ,EAAU,EAAU,SAAS,IACrC,IAAQ,KAAU,IAAQ,EAAO,QAAQ,EAAM,QAAQ,KAAA,GAEvD,IAAY,QACV,EAAU,KAAK,OAAO;EAAE,OAAO,GAAW,EAAE,WAAW;EAAE,OAAO,EAAE;EAAO,EAAE,EACjF,CAAC,EAAU,CACZ,EACK,IAAY,EAAU,SAAS,IAAI,EAAU,EAAU,SAAS,KAAK,KAAA,GAErE,IAAQ,QAAc;AAC1B,MAAI,EAAU,WAAW,EAAG,QAAO;EACnC,IAAM,IAAS,EAAU,KAAK,MAAM,EAAE,MAAM;AAC5C,SAAO;GACL,OAAO,EAAO;GACd,KAAK,KAAK,IAAI,GAAG,EAAO;GACxB,KAAK,KAAK,IAAI,GAAG,EAAO;GACxB,KAAK,EAAO,QAAQ,GAAG,MAAM,IAAI,GAAG,EAAE,GAAG,EAAO;GACjD;IACA,CAAC,EAAU,CAAC,EAET,KAAmD,EAAa,KAAK,OAAO;EAChF,OAAO;EACP,OAAO,EAAgB,EAAE;EAC1B,EAAE,EAEG,UAAmB;AAGvB,EAFA,EAAQ,EAAS,EAAa,CAAC,EAC/B,EAAa,KAAK,EAClB,EAAQ,GAAK;IAGT,KAAS,YAAY;EACzB,IAAM,IAAW,OAAO,EAAK,MAAM;AACnC,MAAI,EAAK,MAAM,MAAM,KAAK,MAAM,CAAC,OAAO,SAAS,EAAS,EAAE;AAC1D,KAAa,mCAAmC;AAChD;;AAEF,IAAa,KAAK;EAClB,IAAM,IAA8B;GAClC,MAAM,EAAK;GACX,OAAO;GACP,MAAM,EAAK,KAAK,MAAM,IAAI,EAAY,EAAK,MAAM;GACjD,YAAY,EAAK,aACb,IAAI,KAAK,EAAK,WAAW,CAAC,aAAa,oBACvC,IAAI,MAAM,EAAC,aAAa;GAC5B,OAAO,EAAK,MAAM,MAAM,GAAG,EAAK,MAAM,MAAM,GAAG;GAC/C,aAAa,EAAK,OAAO,UAAU;GACpC;AACD,MAAI;AAKF,GAJA,MAAM,EAAO,YAAY,EAAM,EAC/B,EAAQ,GAAM,EAEd,EAAgB,EAAK,KAAK,EAC1B,EAAM,OAAO;WACN,GAAG;AACV,KAAa,aAAa,QAAQ,EAAE,UAAU,gCAAgC;;IAI5E,IAAY,EAAM,MAAM,SAAS,EAAE,EACnC,KAAwC;EAC5C;GACE,KAAK;GACL,QAAQ;GACR,OAAO;GACP,YAAY,MAAQ,EAAI;GACxB,SAAS,MACP,kBAAC,QAAD;IAAM,WAAU;cAAhB,CACG,EAAa,EAAI,MAAM,EACxB,kBAAC,QAAD;KAAM,WAAU;eAA+C,EAAI;KAAY,CAAA,CAC1E;;GAEV;EACD;GACE,KAAK;GACL,QAAQ;GACR,YAAY,MAAQ,IAAI,KAAK,EAAI,WAAW,CAAC,SAAS;GACtD,SAAS,MACP,kBAAC,QAAD;IAAM,WAAU;cACb,GAAe,EAAI,WAAW;IAC1B,CAAA;GAEV;EACD;GACE,KAAK;GACL,QAAQ;GACR,cAAc;GACd,SAAS,MACP,EAAI,QACF,kBAAC,QAAD;IAAM,WAAU;cAAuB,EAAI;IAAa,CAAA,GAExD,kBAAC,QAAD;IAAM,WAAU;cAAqB;IAAQ,CAAA;GAElD;EACD;GACE,KAAK;GACL,QAAQ;GACR,SAAS,MACP,EAAI,OAAO,KACT,kBAAC,IAAD,EAAU,QAAQ,EAAI,MAAM,IAAM,CAAA,GAElC,kBAAC,QAAD;IAAM,WAAU;cAAqB;IAAQ,CAAA;GAElD;EACF;AAQD,QACE,kBAAC,GAAD;EACE,OAAM;EACN,aAAY;EACZ,SATF,kBAAC,GAAD;GAAQ,SAAS;aAAjB,CACE,kBAAC,GAAD,EAAM,WAAU,kBAAmB,CAAA,EAAA,sBAC5B;;YAIT;GAME,kBAAC,OAAD;IAAK,WAAU;cACb,kBAAC,IAAD;KAAY,SAAS;KAAe,OAAO;KAAc,UAAU;KAAmB,CAAA;IAClF,CAAA;GAEL,EAAQ,aAAa,EAAY,WAAW,IAC3C,kBAAC,IAAD,EAAa,OAAO,WAAW,EAAgB,EAAa,CAAC,aAAa,CAAC,YAAc,CAAA,GACvF,EAAQ,UACV,kBAAC,GAAD;IACE,SAAQ;IACR,eAAe,EAAQ,SAAS;IAChC,CAAA,GACA,EAAY,WAAW,IACzB,kBAAC,GAAD;IACE,cAAa;IACb,MAAM,kBAAC,GAAD,EAAY,WAAU,aAAc,CAAA;IAC1C,OAAO,MAAM,EAAgB,EAAa,CAAC,aAAa,CAAC;IACzD,aAAY;IACZ,QACE,kBAAC,GAAD;KAAQ,SAAS;eAAjB,CACE,kBAAC,GAAD,EAAM,WAAU,kBAAmB,CAAA,EAAA,sBAC5B;;IAEX,CAAA,GAEF,kBAAC,OAAD;IAAK,WAAU;cAAf;KAEE,kBAAC,EAAO,KAAR;MAAY,GAAI,EAAK,EAAE;gBACrB,kBAAC,GAAD;OACE,WAAU;OACV,WAAU;iBAFZ,CAIE,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,GAAD;UAAU,MAAM,kBAAC,GAAD,EAAY,WAAU,WAAY,CAAA;UAAE,MAAM,EAAK;UAAQ,CAAA,EACvE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,MAAD;UAAI,WAAU;oBACX,EAAgB,EAAa;UAC3B,CAAA,EACL,kBAAC,KAAD;UAAG,WAAU;oBACV,IACG,iBAAiB,GAAmB,EAAO,WAAW,KACtD;UACF,CAAA,CACA,EAAA,CAAA,CACF;YACN,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,KAAD;UAAG,WAAU;oBAAb,CACG,IAAS,EAAa,EAAO,MAAM,GAAG,KACtC,KACC,kBAAC,QAAD;WAAM,WAAU;qBACb,EAAK;WACD,CAAA,CAEP;aACH,KAAS,QACR,kBAAC,OAAD;UAAK,WAAU;oBACb,kBAAC,QAAD;WAAM,WAAU;qBACd,kBAAC,IAAD;YAAkB;YAAO,MAAM,IAAI,EAAK;YAAQ,eAAe;YAAW,CAAA;WACrE,CAAA;UACH,CAAA,CAEJ;WACF;WAEN,kBAAC,OAAD;QAAK,WAAU;kBACb,kBAAC,IAAD;SAAqB,OAAM;SAAO,QAAO;mBACvC,kBAAC,IAAD;UAAW,MAAM;UAAW,QAAQ;WAAE,KAAK;WAAG,OAAO;WAAI,QAAQ;WAAG,MAAM;WAAI;oBAA9E;WACE,kBAAC,QAAD,EAAA,UACE,kBAAC,kBAAD;YAAgB,IAAG;YAAoB,IAAG;YAAI,IAAG;YAAI,IAAG;YAAI,IAAG;sBAA/D;aACE,kBAAC,QAAD;cAAM,QAAO;cAAK,WAAW;cAAc,aAAa;cAAQ,CAAA;aAChE,kBAAC,QAAD;cAAM,QAAO;cAAM,WAAW;cAAc,aAAa;cAAQ,CAAA;aACjE,kBAAC,QAAD;cAAM,QAAO;cAAO,WAAW;cAAc,aAAa;cAAK,CAAA;aAChD;eACZ,CAAA;WACP,kBAAC,IAAD;YACE,QAAO;YACP,iBAAgB;YAChB,UAAU;YACV,CAAA;WACF,kBAAC,IAAD;YACE,SAAQ;YACR,QAAO;YACP,UAAU;YACV,UAAU;YACV,MAAM;aAAE,MAAM;aAA8B,UAAU;aAAI;YAC1D,YAAY;YACZ,YAAY;YACZ,CAAA;WACF,kBAAC,IAAD;YACE,QAAO;YACP,UAAU;YACV,UAAU;YACV,MAAM;aAAE,MAAM;aAA8B,UAAU;aAAI;YAC1D,OAAO;YACP,QAAQ,CAAC,QAAQ,OAAO;YACxB,CAAA;WACF,kBAAC,IAAD;YACE,QAAQ;aAAE,QAAQ;aAA4B,iBAAiB;aAAO;YACtE,cAAc;aACZ,YAAY;aACZ,QAAQ;aACR,cAAc;aACd,OAAO;aACR;YACD,YAAY,EAAE,OAAO,8BAA8B;YACnD,WAAW,EAAE,OAAO,6BAA6B;YACjD,YAAY,MAAU,CACpB,GAAG,EAAa,OAAO,EAAM,CAAC,CAAC,GAAG,EAAK,QACvC,EAAgB,EAAa,CAC9B;YACD,CAAA;WACF,kBAAC,IAAD;YACE,MAAK;YACL,SAAQ;YACR,MAAM,EAAgB,EAAa;YACnC,QAAQ;YACR,aAAa;YACb,MAAK;YACL,aAAa;YACb,KAAK;YACL,WAAW;aACT,GAAG;aACH,aAAa;aACb,QAAQ;aACR,MAAM;aACP;YACD,mBAAmB,CAAC;YACpB,mBAAmB;YACnB,CAAA;WACD,KACC,kBAAC,GAAD;YACE,GAAG,EAAU;YACb,GAAG,EAAU;YACb,GAAG;YACH,SAAA;YACA,YAAW;YACX,MAAM;YACN,aAAa;YACb,QAAO;YACP,CAAA;WAEH,KACC,kBAAC,GAAD;YACE,GAAG,EAAU;YACb,GAAG,EAAU;YACb,GAAG;YACH,SAAA;YACA,YAAW;YACX,MAAM;YACN,QAAO;YACP,aAAa;YACb,CAAA;WAEM;;SACQ,CAAA;QAClB,CAAA,CACI;;MACD,CAAA;KAGZ,KACC,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,OAAD;MAAK,WAAU;gBAAf;OACE,kBAAC,EAAO,KAAR;QAAY,GAAI,EAAK,IAAK;kBACxB,kBAAC,GAAD;SACE,WAAU;SACV,MAAM,kBAAC,GAAD;UAAU,MAAM,kBAAC,IAAD,EAAU,WAAU,WAAY,CAAA;UAAE,MAAM;UAAgB,CAAA;SAC9E,OAAO,EAAa,EAAM,MAAM;SAChC,OAAM;SACN,CAAA;QACS,CAAA;OACb,kBAAC,EAAO,KAAR;QAAY,GAAI,EAAK,IAAK;kBACxB,kBAAC,GAAD;SACE,WAAU;SACV,MAAM,kBAAC,GAAD;UAAU,MAAM,kBAAC,IAAD,EAAO,WAAU,WAAY,CAAA;UAAE,MAAM;UAAgB,CAAA;SAC3E,OAAO,GAAG,EAAa,EAAM,KAAK,EAAE,uBAAuB,GAAG,CAAC,CAAC,GAAG,EAAK;SACxE,OAAO,kBAAkB,EAAgB,EAAa,CAAC,aAAa;SACpE,CAAA;QACS,CAAA;OACb,kBAAC,EAAO,KAAR;QAAY,GAAI,EAAK,GAAI;kBACvB,kBAAC,GAAD;SACE,WAAU;SACV,MACE,kBAAC,GAAD;UAAU,MAAM,kBAAC,IAAD,EAAa,WAAU,WAAY,CAAA;UAAE,MAAM;UAAgB,CAAA;SAE7E,OAAO,GAAG,EAAa,EAAM,IAAI,CAAC,GAAG,EAAa,EAAM,IAAI;SAC5D,OAAO,iBAAiB,EAAK,KAAK;SAClC,CAAA;QACS,CAAA;OACT;SACN,kBAAC,KAAD;MAAG,WAAU;gBAAb;OAA+C;OAClB,EAAa,EAAM,MAAM;OAAE;OACrD,EAAgB,EAAa,CAAC,aAAa;OAAC;OAC5C,EAAM,UAAU,IAAI,KAAK;OAAI;OAAS,EAAa,EAAa;OAAC;OAEhE;QACA,EAAA,CAAA;KAIR,kBAAC,EAAO,KAAR;MAAY,GAAI,EAAK,KAAM,GAAM;gBAC/B,kBAAC,IAAD;OAAS,OAAM;OAAkB,aAAY;iBAC1C,EAAM,UACL,kBAAC,GAAD;QACE,SAAQ;QACR,eAAe,EAAM,SAAS;QAC9B,CAAA,GACA,EAAM,aAAa,EAAU,WAAW,IAC1C,kBAAC,OAAD;QAAK,WAAU;kBAA+C;QAExD,CAAA,GACJ,EAAU,WAAW,IACvB,kBAAC,GAAD;QACE,cAAa;QACb,MAAM,kBAAC,GAAD,EAAY,WAAU,aAAc,CAAA;QAC1C,OAAM;QACN,aAAY;QACZ,CAAA,GAEF,kBAAA,IAAA,EAAA,UAAA,CACE,kBAAC,IAAD;QACE,SAAS;QACT,MAAM;QACN,SAAS,MAAQ,EAAI;QACrB,aAAa;SAAE,KAAK;SAAc,KAAK;SAAQ;QAC/C,CAAA,EACF,kBAAC,IAAD;QACE,MAAM,EAAM;QACZ,UAAU;QACV,WAAW,EAAU;QACrB,SAAS,EAAM,MAAM,UAAU,WAAW;QAC1C,eAAe,MAAM,EAAM,aAAa,EAAE;QAC1C,CAAA,CACD,EAAA,CAAA;OAEG,CAAA;MACC,CAAA;KACT;;GAIR,kBAAC,IAAD;IAAwB;IAAM,cAAc;cAC1C,kBAAC,IAAD,EAAA,UAAA;KACE,kBAAC,IAAD,EAAA,UAAA,CACE,kBAAC,IAAD,EAAA,UAAuB,sBAA0C,CAAA,EACjE,kBAAC,IAAD,EAAA,UAA6B,mEAEC,CAAA,CACP,EAAA,CAAA;KAEzB,kBAAC,OAAD;MAAK,WAAU;gBAAf;OACE,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,GAAD;SAAO,SAAQ;mBAAkB;SAAc,CAAA,EAC/C,kBAAC,GAAD;SACE,OAAO,EAAK;SACZ,gBAAgB,MAAM;UAEpB,IAAM,IAAW;AACjB,aAAS,OAAU;WACjB,GAAG;WACH,MAAM;WACN,MAAM,EAAY,GAAU;WAC7B,EAAE;;mBATP,CAYE,kBAAC,IAAD;UAAe,IAAG;oBAChB,kBAAC,IAAD,EAAa,aAAY,mBAAoB,CAAA;UAC/B,CAAA,EAChB,kBAAC,GAAD,EAAA,UACG,EAAa,KAAK,MACjB,kBAAC,GAAD;UAAoB,OAAO;oBACxB,EAAgB,EAAE;UACR,EAFI,EAEJ,CACb,EACY,CAAA,CACT;WACL;;OAEN,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,GAAD;UAAO,SAAQ;oBAAmB;UAAa,CAAA,EAC/C,kBAAC,GAAD;UACE,IAAG;UACH,MAAK;UACL,WAAU;UACV,MAAK;UACL,OAAO,EAAK;UACZ,WAAW,MAAM,GAAS,OAAU;WAAE,GAAG;WAAM,OAAO,EAAE,OAAO;WAAO,EAAE;UACxE,aAAY;UACZ,CAAA,CACE;YACN,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,GAAD;UAAO,SAAQ;oBAAkB;UAAY,CAAA,EAC7C,kBAAC,GAAD;UACE,IAAG;UACH,OAAO,EAAK;UACZ,WAAW,MAAM,GAAS,OAAU;WAAE,GAAG;WAAM,MAAM,EAAE,OAAO;WAAO,EAAE;UACvE,aAAa,EAAY,EAAK,MAAM;UACpC,CAAA,CACE;WACF;;OAEN,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,GAAD;SAAO,SAAQ;mBAAsB;SAAmB,CAAA,EACxD,kBAAC,GAAD;SACE,IAAG;SACH,MAAK;SACL,OAAO,EAAK;SACZ,WAAW,MAAM,GAAS,OAAU;UAAE,GAAG;UAAM,YAAY,EAAE,OAAO;UAAO,EAAE;SAC7E,CAAA,CACE;;OAEN,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,GAAD;SAAO,SAAQ;mBAAmB;SAAa,CAAA,EAC/C,kBAAC,IAAD;SACE,IAAG;SACH,OAAO,EAAK;SACZ,WAAW,MAAM,GAAS,OAAU;UAAE,GAAG;UAAM,OAAO,EAAE,OAAO;UAAO,EAAE;SACxE,aAAY;SACZ,MAAM;SACN,CAAA,CACE;;OAIN,kBAAC,IAAD;QACE,IAAG;QACH,OAAM;QACN,OAAO,EAAK;QACZ,WAAW,MAAU,GAAS,OAAU;SAAE,GAAG;SAAM;SAAO,EAAE;QAC5D,QAAO;QACP,UAAU,EAAO;QACjB,MAAK;QACL,CAAA;OAED,KAAa,kBAAC,KAAD;QAAG,WAAU;kBAAkC;QAAc,CAAA;OACvE;;KAEN,kBAAC,IAAD,EAAA,UAAA,CACE,kBAAC,GAAD;MAAQ,SAAQ;MAAU,eAAe,EAAQ,GAAM;MAAE,UAAU,EAAO;gBAAW;MAE5E,CAAA,EACT,kBAAC,GAAD;MAAQ,SAAS;MAAQ,UAAU,EAAO;gBACvC,EAAO,YAAY,eAAe;MAC5B,CAAA,CACc,EAAA,CAAA;KACD,EAAA,CAAA;IACT,CAAA;GACR"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Recovery — sleep, readiness, soreness, stress & mood tracking.
3
+ * @module mybodyshield/pages/recovery
4
+ *
5
+ * Reads the real `mybListRecoveryLogs` array endpoint (no pagination). KPIs pull
6
+ * the readiness / sleep / mood metrics independently so they stay stable while
7
+ * the FilterTabs drive the charted + tabulated metric. New entries go through
8
+ * `mybLogRecovery`. No mock data — everything is derived from the hooks.
9
+ */
10
+ export declare function RecoveryPage(): import("react").JSX.Element;
11
+ //# sourceMappingURL=RecoveryPage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RecoveryPage.d.ts","sourceRoot":"","sources":["../../../../src/mybodyshield/pages/recovery/RecoveryPage.tsx"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AA+FH,wBAAgB,YAAY,gCA4hB3B"}
@@ -1,20 +1,20 @@
1
- import { motion as e } from "../../../node_modules/framer-motion/dist/es/render/components/motion/proxy.js";
2
- import { useReducedMotion as t } from "../../../node_modules/framer-motion/dist/es/utils/reduced-motion/use-reduced-motion.js";
3
- import { PageLayout as n } from "../../components/PageLayout.js";
4
- import { MetricsCard as r } from "../../components/MetricsCard.js";
5
- import { formatDate as ee, formatNumber as i, formatRelativeTime as a } from "../../utils/formatters.js";
6
- import { ErrorView as o, LoadingView as te, Section as s } from "../../components/StateViews.js";
7
- import { EmptyState as ne } from "../../components/EmptyState.js";
8
- import { IconChip as c, RECOVERY_META as l } from "../_shared/visuals.js";
9
- import { useMybLogRecovery as re, useMybRecoveryLogs as u } from "../../hooks/useMyBodyShieldApi.js";
1
+ import { PageLayout as e } from "../../components/PageLayout.js";
2
+ import { MetricsCard as t } from "../../components/MetricsCard.js";
3
+ import { formatDate as n, formatNumber as r, formatRelativeTime as i } from "../../utils/formatters.js";
4
+ import { EmptyState as ee } from "../../components/EmptyState.js";
5
+ import { ErrorView as a, LoadingView as te, Section as o } from "../../components/StateViews.js";
6
+ import { DataTable as ne } from "../../components/DataTable.js";
7
+ import { FilterTabs as re } from "../../components/FilterTabs.js";
8
+ import { useMybLogRecovery as ie, useMybRecoveryLogs as s } from "../../hooks/useMyBodyShieldApi.js";
10
9
  import "../../hooks/index.js";
11
- import { DataTable as ie } from "../../components/DataTable.js";
12
- import { FilterTabs as ae } from "../../components/FilterTabs.js";
13
10
  import "../../components/index.js";
14
- import { Tooltip as d } from "../../../node_modules/recharts/es6/component/Tooltip.js";
15
- import { ResponsiveContainer as oe } from "../../../node_modules/recharts/es6/component/ResponsiveContainer.js";
16
- import { ReferenceDot as f } from "../../../node_modules/recharts/es6/cartesian/ReferenceDot.js";
17
- import { CartesianGrid as p } from "../../../node_modules/recharts/es6/cartesian/CartesianGrid.js";
11
+ import { motion as c } from "../../../node_modules/framer-motion/dist/es/render/components/motion/proxy.js";
12
+ import { useReducedMotion as l } from "../../../node_modules/framer-motion/dist/es/utils/reduced-motion/use-reduced-motion.js";
13
+ import { IconChip as u, RECOVERY_META as d } from "../_shared/visuals.js";
14
+ import { Tooltip as f } from "../../../node_modules/recharts/es6/component/Tooltip.js";
15
+ import { ResponsiveContainer as ae } from "../../../node_modules/recharts/es6/component/ResponsiveContainer.js";
16
+ import { ReferenceDot as p } from "../../../node_modules/recharts/es6/cartesian/ReferenceDot.js";
17
+ import { CartesianGrid as oe } from "../../../node_modules/recharts/es6/cartesian/CartesianGrid.js";
18
18
  import { Area as se } from "../../../node_modules/recharts/es6/cartesian/Area.js";
19
19
  import { XAxis as ce } from "../../../node_modules/recharts/es6/cartesian/XAxis.js";
20
20
  import { YAxis as le } from "../../../node_modules/recharts/es6/cartesian/YAxis.js";
@@ -22,7 +22,7 @@ import { AreaChart as ue } from "../../../node_modules/recharts/es6/chart/AreaCh
22
22
  import { useMemo as m, useState as h } from "react";
23
23
  import { HeartPulse as g, Plus as _ } from "lucide-react";
24
24
  import { jsx as v, jsxs as y } from "react/jsx-runtime";
25
- import { Button as b, GlassCard as de, Input as x, Label as S, ResponsiveDialog as fe, ResponsiveDialogContent as pe, ResponsiveDialogDescription as me, ResponsiveDialogFooter as C, ResponsiveDialogHeader as w, ResponsiveDialogTitle as he, Select as ge, SelectContent as _e, SelectItem as ve, SelectTrigger as ye, SelectValue as be, Spinner as xe, Textarea as Se } from "@burdenoff/fe-libs/ui";
25
+ import { Button as b, GlassCard as de, Input as x, Label as S, ResponsiveDialog as fe, ResponsiveDialogContent as pe, ResponsiveDialogDescription as C, ResponsiveDialogFooter as w, ResponsiveDialogHeader as me, ResponsiveDialogTitle as he, Select as ge, SelectContent as _e, SelectItem as ve, SelectTrigger as ye, SelectValue as be, Spinner as xe, Textarea as Se } from "@burdenoff/fe-libs/ui";
26
26
  //#region src/mybodyshield/pages/recovery/RecoveryPage.tsx
27
27
  var T = [
28
28
  "SLEEP",
@@ -35,7 +35,7 @@ var T = [
35
35
  label: "All"
36
36
  }, ...T.map((e) => ({
37
37
  value: e,
38
- label: l[e].label
38
+ label: d[e].label
39
39
  }))], E = "var(--color-action-primary-bg)", D = (e) => e >= 70 ? "success" : e >= 40 ? "warning" : "error", O = (e) => e >= 7 ? "success" : e >= 6 ? "warning" : "error";
40
40
  function k(e) {
41
41
  let t = (e) => String(e).padStart(2, "0");
@@ -49,7 +49,7 @@ var A = (e = "READINESS") => ({
49
49
  loggedAt: k(/* @__PURE__ */ new Date())
50
50
  });
51
51
  function j() {
52
- let [k, j] = h("ALL"), [we, M] = h(!1), [N, P] = h(A), [F, I] = h(null), Te = u("READINESS"), Ee = u("SLEEP"), De = u("MOOD"), L = u(k === "ALL" ? void 0 : k), R = re(), z = t(), B = (e, t = !0) => z ? { initial: !1 } : {
52
+ let [k, j] = h("ALL"), [we, M] = h(!1), [N, P] = h(A), [F, I] = h(null), Te = s("READINESS"), Ee = s("SLEEP"), De = s("MOOD"), L = s(k === "ALL" ? void 0 : k), R = ie(), z = l(), B = (e, t = !0) => z ? { initial: !1 } : {
53
53
  initial: {
54
54
  opacity: 0,
55
55
  y: 14
@@ -71,8 +71,8 @@ function j() {
71
71
  damping: 22
72
72
  }
73
73
  } } : {}
74
- }, V = Te.data ?? [], H = Ee.data ?? [], U = De.data ?? [], W = L.data ?? [], G = m(() => [...V].sort((e, t) => new Date(t.loggedAt).getTime() - new Date(e.loggedAt).getTime())[0], [V]), K = m(() => [...U].sort((e, t) => new Date(t.loggedAt).getTime() - new Date(e.loggedAt).getTime())[0], [U]), q = m(() => H.length === 0 ? null : H.reduce((e, t) => e + t.sleepHours, 0) / H.length, [H]), J = k === "SLEEP", Y = k === "ALL" ? "All metrics" : l[k].label, X = m(() => [...W].sort((e, t) => new Date(e.loggedAt).getTime() - new Date(t.loggedAt).getTime()).map((e) => ({
75
- label: ee(e.loggedAt),
74
+ }, V = Te.data ?? [], H = Ee.data ?? [], U = De.data ?? [], W = L.data ?? [], G = m(() => [...V].sort((e, t) => new Date(t.loggedAt).getTime() - new Date(e.loggedAt).getTime())[0], [V]), K = m(() => [...U].sort((e, t) => new Date(t.loggedAt).getTime() - new Date(e.loggedAt).getTime())[0], [U]), q = m(() => H.length === 0 ? null : H.reduce((e, t) => e + t.sleepHours, 0) / H.length, [H]), J = k === "SLEEP", Y = k === "ALL" ? "All metrics" : d[k].label, X = m(() => [...W].sort((e, t) => new Date(e.loggedAt).getTime() - new Date(t.loggedAt).getTime()).map((e) => ({
75
+ label: n(e.loggedAt),
76
76
  value: J ? e.sleepHours : e.score
77
77
  })), [W, J]), Z = X.length > 0 ? X[X.length - 1] : void 0, Q = (e = "READINESS") => {
78
78
  P(A(e)), I(null), M(!0);
@@ -114,12 +114,12 @@ function j() {
114
114
  {
115
115
  key: "metric",
116
116
  header: "Metric",
117
- sortValue: (e) => l[e.metric].label,
117
+ sortValue: (e) => d[e.metric].label,
118
118
  render: (e) => {
119
- let t = l[e.metric], n = t.icon;
119
+ let t = d[e.metric], n = t.icon;
120
120
  return /* @__PURE__ */ y("span", {
121
121
  className: "inline-flex items-center gap-2.5",
122
- children: [/* @__PURE__ */ v(c, {
122
+ children: [/* @__PURE__ */ v(u, {
123
123
  icon: /* @__PURE__ */ v(n, { className: "h-4 w-4" }),
124
124
  tint: t.tint
125
125
  }), /* @__PURE__ */ v("span", {
@@ -136,10 +136,10 @@ function j() {
136
136
  sortValue: (e) => e.metric === "SLEEP" ? e.sleepHours : e.score,
137
137
  render: (e) => e.metric === "SLEEP" ? /* @__PURE__ */ y("span", {
138
138
  className: "tabular-nums text-text-primary",
139
- children: [i(e.sleepHours, { maximumFractionDigits: 1 }), " h"]
139
+ children: [r(e.sleepHours, { maximumFractionDigits: 1 }), " h"]
140
140
  }) : /* @__PURE__ */ v("span", {
141
141
  className: "tabular-nums text-text-primary",
142
- children: i(e.score)
142
+ children: r(e.score)
143
143
  })
144
144
  },
145
145
  {
@@ -161,11 +161,11 @@ function j() {
161
161
  sortValue: (e) => new Date(e.loggedAt).getTime(),
162
162
  render: (e) => /* @__PURE__ */ v("span", {
163
163
  className: "whitespace-nowrap text-text-tertiary",
164
- children: a(e.loggedAt)
164
+ children: i(e.loggedAt)
165
165
  })
166
166
  }
167
- ], Ae = l.READINESS.icon, je = l.SLEEP.icon, Me = l.MOOD.icon;
168
- return /* @__PURE__ */ y(n, {
167
+ ], Ae = d.READINESS.icon, je = d.SLEEP.icon, Me = d.MOOD.icon;
168
+ return /* @__PURE__ */ y(e, {
169
169
  title: "Recovery",
170
170
  description: "Sleep, readiness, soreness, stress & mood.",
171
171
  actions: /* @__PURE__ */ y(b, {
@@ -174,21 +174,21 @@ function j() {
174
174
  children: [/* @__PURE__ */ v(_, { className: "h-4 w-4" }), "Log recovery"]
175
175
  }),
176
176
  children: [
177
- /* @__PURE__ */ y(e.div, {
177
+ /* @__PURE__ */ y(c.div, {
178
178
  ...B(0),
179
179
  className: "grid grid-cols-1 gap-4 sm:grid-cols-3",
180
180
  children: [
181
- /* @__PURE__ */ v(r, {
182
- icon: /* @__PURE__ */ v(c, {
181
+ /* @__PURE__ */ v(t, {
182
+ icon: /* @__PURE__ */ v(u, {
183
183
  icon: /* @__PURE__ */ v(Ae, { className: "h-5 w-5" }),
184
- tint: l.READINESS.tint
184
+ tint: d.READINESS.tint
185
185
  }),
186
- value: G ? i(G.score) : "—",
186
+ value: G ? r(G.score) : "—",
187
187
  label: "Latest readiness",
188
188
  accent: G ? D(G.score) : void 0,
189
189
  footer: G ? /* @__PURE__ */ v("p", {
190
190
  className: "text-xs text-text-tertiary",
191
- children: a(G.loggedAt)
191
+ children: i(G.loggedAt)
192
192
  }) : /* @__PURE__ */ v("button", {
193
193
  type: "button",
194
194
  onClick: () => Q("READINESS"),
@@ -196,12 +196,12 @@ function j() {
196
196
  children: "Log readiness →"
197
197
  })
198
198
  }),
199
- /* @__PURE__ */ v(r, {
200
- icon: /* @__PURE__ */ v(c, {
199
+ /* @__PURE__ */ v(t, {
200
+ icon: /* @__PURE__ */ v(u, {
201
201
  icon: /* @__PURE__ */ v(je, { className: "h-5 w-5" }),
202
- tint: l.SLEEP.tint
202
+ tint: d.SLEEP.tint
203
203
  }),
204
- value: q == null ? "—" : `${i(q, { maximumFractionDigits: 1 })} h`,
204
+ value: q == null ? "—" : `${r(q, { maximumFractionDigits: 1 })} h`,
205
205
  label: "Avg sleep",
206
206
  accent: q == null ? void 0 : O(q),
207
207
  footer: H.length > 0 ? /* @__PURE__ */ y("p", {
@@ -222,17 +222,17 @@ function j() {
222
222
  children: "Log sleep →"
223
223
  })
224
224
  }),
225
- /* @__PURE__ */ v(r, {
226
- icon: /* @__PURE__ */ v(c, {
225
+ /* @__PURE__ */ v(t, {
226
+ icon: /* @__PURE__ */ v(u, {
227
227
  icon: /* @__PURE__ */ v(Me, { className: "h-5 w-5" }),
228
- tint: l.MOOD.tint
228
+ tint: d.MOOD.tint
229
229
  }),
230
- value: K ? i(K.score) : "—",
230
+ value: K ? r(K.score) : "—",
231
231
  label: "Latest mood",
232
232
  accent: K ? D(K.score) : void 0,
233
233
  footer: K ? /* @__PURE__ */ v("p", {
234
234
  className: "text-xs text-text-tertiary",
235
- children: a(K.loggedAt)
235
+ children: i(K.loggedAt)
236
236
  }) : /* @__PURE__ */ v("button", {
237
237
  type: "button",
238
238
  onClick: () => Q("MOOD"),
@@ -244,16 +244,16 @@ function j() {
244
244
  }),
245
245
  /* @__PURE__ */ v("div", {
246
246
  className: "mt-6 flex flex-wrap items-center gap-3",
247
- children: /* @__PURE__ */ v(ae, {
247
+ children: /* @__PURE__ */ v(re, {
248
248
  options: Ce,
249
249
  value: k,
250
250
  onChange: j
251
251
  })
252
252
  }),
253
- /* @__PURE__ */ v(s, {
253
+ /* @__PURE__ */ v(o, {
254
254
  title: `${Y} trend`,
255
255
  description: J ? "Sleep hours over time." : "Recovery score over time.",
256
- children: /* @__PURE__ */ v(e.div, {
256
+ children: /* @__PURE__ */ v(c.div, {
257
257
  ...B(.08),
258
258
  children: /* @__PURE__ */ v(de, {
259
259
  treatment: "glass",
@@ -261,7 +261,7 @@ function j() {
261
261
  children: L.isLoading ? /* @__PURE__ */ v("div", {
262
262
  className: "flex h-[280px] items-center justify-center",
263
263
  children: /* @__PURE__ */ v(xe, { size: "lg" })
264
- }) : L.isError ? /* @__PURE__ */ v(o, {
264
+ }) : L.isError ? /* @__PURE__ */ v(a, {
265
265
  message: "We couldn't load this metric's history.",
266
266
  onRetry: () => L.refetch()
267
267
  }) : X.length === 0 ? /* @__PURE__ */ y("div", {
@@ -284,7 +284,7 @@ function j() {
284
284
  ]
285
285
  })
286
286
  ]
287
- }) : /* @__PURE__ */ v(oe, {
287
+ }) : /* @__PURE__ */ v(ae, {
288
288
  width: "100%",
289
289
  height: 280,
290
290
  children: /* @__PURE__ */ y(ue, {
@@ -320,7 +320,7 @@ function j() {
320
320
  })
321
321
  ]
322
322
  }) }),
323
- /* @__PURE__ */ v(p, {
323
+ /* @__PURE__ */ v(oe, {
324
324
  vertical: !1,
325
325
  stroke: "var(--color-border-seam)",
326
326
  strokeDasharray: "4 4"
@@ -345,7 +345,7 @@ function j() {
345
345
  fontSize: 12
346
346
  }
347
347
  }),
348
- /* @__PURE__ */ v(d, {
348
+ /* @__PURE__ */ v(f, {
349
349
  contentStyle: {
350
350
  background: "var(--color-bg-surface)",
351
351
  border: "1px solid var(--color-border-seam)",
@@ -377,7 +377,7 @@ function j() {
377
377
  isAnimationActive: !z,
378
378
  animationDuration: 900
379
379
  }),
380
- Z && /* @__PURE__ */ v(f, {
380
+ Z && /* @__PURE__ */ v(p, {
381
381
  x: Z.label,
382
382
  y: Z.value,
383
383
  r: 12,
@@ -387,7 +387,7 @@ function j() {
387
387
  fillOpacity: .16,
388
388
  stroke: "none"
389
389
  }),
390
- Z && /* @__PURE__ */ v(f, {
390
+ Z && /* @__PURE__ */ v(p, {
391
391
  x: Z.label,
392
392
  y: Z.value,
393
393
  r: 5,
@@ -403,15 +403,15 @@ function j() {
403
403
  })
404
404
  })
405
405
  }),
406
- /* @__PURE__ */ v(e.div, {
406
+ /* @__PURE__ */ v(c.div, {
407
407
  ...B(.16, !1),
408
- children: /* @__PURE__ */ v(s, {
408
+ children: /* @__PURE__ */ v(o, {
409
409
  title: "Recovery log",
410
410
  description: "Every recovery entry, most recent first.",
411
- children: L.isLoading ? /* @__PURE__ */ v(te, { label: "Loading recovery logs…" }) : L.isError ? /* @__PURE__ */ v(o, {
411
+ children: L.isLoading ? /* @__PURE__ */ v(te, { label: "Loading recovery logs…" }) : L.isError ? /* @__PURE__ */ v(a, {
412
412
  message: "We couldn't load your recovery logs.",
413
413
  onRetry: () => L.refetch()
414
- }) : W.length === 0 ? /* @__PURE__ */ v(ne, {
414
+ }) : W.length === 0 ? /* @__PURE__ */ v(ee, {
415
415
  illustration: "empty-data",
416
416
  icon: /* @__PURE__ */ v(g, { className: "h-10 w-10" }),
417
417
  title: "No recovery logs yet",
@@ -421,7 +421,7 @@ function j() {
421
421
  onClick: () => Q(),
422
422
  children: [/* @__PURE__ */ v(_, { className: "h-4 w-4" }), "Log recovery"]
423
423
  })
424
- }) : /* @__PURE__ */ v(ie, {
424
+ }) : /* @__PURE__ */ v(ne, {
425
425
  columns: ke,
426
426
  rows: W,
427
427
  rowKey: (e) => e.id,
@@ -438,7 +438,7 @@ function j() {
438
438
  children: /* @__PURE__ */ y(pe, {
439
439
  className: "sm:max-w-[480px]",
440
440
  children: [
441
- /* @__PURE__ */ y(w, { children: [/* @__PURE__ */ v(he, { children: "Log recovery" }), /* @__PURE__ */ v(me, { children: "Capture how your body is recovering — one metric per entry." })] }),
441
+ /* @__PURE__ */ y(me, { children: [/* @__PURE__ */ v(he, { children: "Log recovery" }), /* @__PURE__ */ v(C, { children: "Capture how your body is recovering — one metric per entry." })] }),
442
442
  /* @__PURE__ */ y("div", {
443
443
  className: "space-y-4 py-2",
444
444
  children: [
@@ -458,7 +458,7 @@ function j() {
458
458
  children: /* @__PURE__ */ v(be, {})
459
459
  }), /* @__PURE__ */ v(_e, { children: T.map((e) => /* @__PURE__ */ v(ve, {
460
460
  value: e,
461
- children: l[e].label
461
+ children: d[e].label
462
462
  }, e)) })]
463
463
  })]
464
464
  }),
@@ -551,7 +551,7 @@ function j() {
551
551
  })
552
552
  ]
553
553
  }),
554
- /* @__PURE__ */ y(C, { children: [/* @__PURE__ */ v(b, {
554
+ /* @__PURE__ */ y(w, { children: [/* @__PURE__ */ v(b, {
555
555
  variant: "outline",
556
556
  onClick: () => M(!1),
557
557
  disabled: R.isPending,