@burdenoff/microfe-mybodyshield 2026.827.1 → 2026.830.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/mybodyshield/MyBodyShieldRoot.d.ts.map +1 -1
- package/dist/mybodyshield/MyBodyShieldRoot.js +54 -56
- package/dist/mybodyshield/MyBodyShieldRoot.js.map +1 -1
- package/dist/mybodyshield/MyBodyShieldRoutes.d.ts.map +1 -1
- package/dist/mybodyshield/MyBodyShieldRoutes.js +121 -62
- package/dist/mybodyshield/MyBodyShieldRoutes.js.map +1 -1
- package/dist/mybodyshield/components/EvidenceUpload.d.ts.map +1 -1
- package/dist/mybodyshield/components/EvidenceUpload.js +46 -45
- package/dist/mybodyshield/components/EvidenceUpload.js.map +1 -1
- package/dist/mybodyshield/components/FileLink.d.ts.map +1 -1
- package/dist/mybodyshield/components/FileLink.js +27 -18
- package/dist/mybodyshield/components/FileLink.js.map +1 -1
- package/dist/mybodyshield/components/MybDiscussion.d.ts +1 -1
- package/dist/mybodyshield/components/MybDiscussion.d.ts.map +1 -1
- package/dist/mybodyshield/components/MybDiscussion.js +59 -58
- package/dist/mybodyshield/components/MybDiscussion.js.map +1 -1
- package/dist/mybodyshield/components/MybTags.d.ts +1 -1
- package/dist/mybodyshield/components/MybTags.d.ts.map +1 -1
- package/dist/mybodyshield/components/MybTags.js +83 -74
- package/dist/mybodyshield/components/MybTags.js.map +1 -1
- package/dist/mybodyshield/components/PageDisabled.d.ts.map +1 -1
- package/dist/mybodyshield/components/PageDisabled.js +16 -11
- package/dist/mybodyshield/components/PageDisabled.js.map +1 -1
- package/dist/mybodyshield/components/Pagination.d.ts.map +1 -1
- package/dist/mybodyshield/components/Pagination.js +36 -27
- package/dist/mybodyshield/components/Pagination.js.map +1 -1
- package/dist/mybodyshield/components/StateViews.d.ts.map +1 -1
- package/dist/mybodyshield/components/StateViews.js +42 -34
- package/dist/mybodyshield/components/StateViews.js.map +1 -1
- package/dist/mybodyshield/components/UserSearchSelect.d.ts.map +1 -1
- package/dist/mybodyshield/components/UserSearchSelect.js +66 -68
- package/dist/mybodyshield/components/UserSearchSelect.js.map +1 -1
- package/dist/mybodyshield/hooks/index.d.ts +1 -0
- package/dist/mybodyshield/hooks/index.d.ts.map +1 -1
- package/dist/mybodyshield/hooks/index.js +1 -0
- package/dist/mybodyshield/hooks/useTr.d.ts +5 -0
- package/dist/mybodyshield/hooks/useTr.d.ts.map +1 -0
- package/dist/mybodyshield/hooks/useTr.js +12 -0
- package/dist/mybodyshield/hooks/useTr.js.map +1 -0
- package/dist/mybodyshield/pages/DashboardPage.d.ts.map +1 -1
- package/dist/mybodyshield/pages/DashboardPage.js +157 -136
- package/dist/mybodyshield/pages/DashboardPage.js.map +1 -1
- package/dist/mybodyshield/pages/SettingsPage.d.ts.map +1 -1
- package/dist/mybodyshield/pages/SettingsPage.js +50 -59
- package/dist/mybodyshield/pages/SettingsPage.js.map +1 -1
- package/dist/mybodyshield/pages/_shared/visuals.d.ts +2 -1
- package/dist/mybodyshield/pages/_shared/visuals.d.ts.map +1 -1
- package/dist/mybodyshield/pages/_shared/visuals.js +91 -81
- package/dist/mybodyshield/pages/_shared/visuals.js.map +1 -1
- package/dist/mybodyshield/pages/ai/AiControlsPage.d.ts.map +1 -1
- package/dist/mybodyshield/pages/ai/AiControlsPage.js +255 -266
- package/dist/mybodyshield/pages/ai/AiControlsPage.js.map +1 -1
- package/dist/mybodyshield/pages/coaching/CoachingPage.d.ts.map +1 -1
- package/dist/mybodyshield/pages/coaching/CoachingPage.js +321 -301
- package/dist/mybodyshield/pages/coaching/CoachingPage.js.map +1 -1
- package/dist/mybodyshield/pages/goals/GoalsPage.d.ts.map +1 -1
- package/dist/mybodyshield/pages/goals/GoalsPage.js +247 -251
- package/dist/mybodyshield/pages/goals/GoalsPage.js.map +1 -1
- package/dist/mybodyshield/pages/insights/InsightsCharts.d.ts.map +1 -1
- package/dist/mybodyshield/pages/insights/InsightsCharts.js +216 -213
- package/dist/mybodyshield/pages/insights/InsightsCharts.js.map +1 -1
- package/dist/mybodyshield/pages/insights/InsightsPage.d.ts.map +1 -1
- package/dist/mybodyshield/pages/insights/InsightsPage.js +290 -284
- package/dist/mybodyshield/pages/insights/InsightsPage.js.map +1 -1
- package/dist/mybodyshield/pages/nutrition/NutritionPage.d.ts.map +1 -1
- package/dist/mybodyshield/pages/nutrition/NutritionPage.js +575 -529
- package/dist/mybodyshield/pages/nutrition/NutritionPage.js.map +1 -1
- package/dist/mybodyshield/pages/programs/ProgramDetailPage.d.ts.map +1 -1
- package/dist/mybodyshield/pages/programs/ProgramDetailPage.js +333 -275
- package/dist/mybodyshield/pages/programs/ProgramDetailPage.js.map +1 -1
- package/dist/mybodyshield/pages/programs/ProgramsPage.d.ts.map +1 -1
- package/dist/mybodyshield/pages/programs/ProgramsPage.js +258 -253
- package/dist/mybodyshield/pages/programs/ProgramsPage.js.map +1 -1
- package/dist/mybodyshield/pages/progress/ProgressPage.d.ts.map +1 -1
- package/dist/mybodyshield/pages/progress/ProgressPage.js +271 -254
- package/dist/mybodyshield/pages/progress/ProgressPage.js.map +1 -1
- package/dist/mybodyshield/pages/recovery/RecoveryPage.d.ts.map +1 -1
- package/dist/mybodyshield/pages/recovery/RecoveryPage.js +206 -200
- package/dist/mybodyshield/pages/recovery/RecoveryPage.js.map +1 -1
- package/dist/mybodyshield/pages/train/SessionDetailPage.d.ts.map +1 -1
- package/dist/mybodyshield/pages/train/SessionDetailPage.js +327 -294
- package/dist/mybodyshield/pages/train/SessionDetailPage.js.map +1 -1
- package/dist/mybodyshield/pages/train/SessionsPage.d.ts.map +1 -1
- package/dist/mybodyshield/pages/train/SessionsPage.js +180 -163
- package/dist/mybodyshield/pages/train/SessionsPage.js.map +1 -1
- package/dist/mybodyshield/utils/i18n.d.ts +1 -2
- package/dist/mybodyshield/utils/i18n.d.ts.map +1 -1
- package/dist/mybodyshield/utils/i18n.js +15 -0
- package/dist/mybodyshield/utils/i18n.js.map +1 -0
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RecoveryPage.js","names":[],"sources":["../../../../src/mybodyshield/pages/recovery/RecoveryPage.tsx"],"sourcesContent":["/**\n * Recovery — sleep, readiness, soreness, stress & mood tracking.\n * @module mybodyshield/pages/recovery\n *\n * Reads the real `mybListRecoveryLogs` array endpoint (no pagination). KPIs pull\n * the readiness / sleep / mood metrics independently so they stay stable while\n * the FilterTabs drive the charted + tabulated metric. New entries go through\n * `mybLogRecovery`. No mock data — everything is derived from the hooks.\n */\n\nimport { useMemo, useState } from 'react';\nimport { Plus, HeartPulse } from 'lucide-react';\nimport {\n ResponsiveContainer,\n AreaChart,\n Area,\n XAxis,\n YAxis,\n CartesianGrid,\n Tooltip,\n ReferenceDot,\n} from 'recharts';\nimport { motion, useReducedMotion, type MotionProps } from 'framer-motion';\nimport {\n Button,\n Input,\n Label,\n Textarea,\n Select,\n SelectTrigger,\n SelectContent,\n SelectItem,\n SelectValue,\n GlassCard,\n Spinner,\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 LoadingView,\n ErrorView,\n Section,\n EmptyState,\n type Column,\n type FilterOption,\n} from '../../components';\nimport { useMybRecoveryLogs, useMybLogRecovery } from '../../hooks';\nimport type { MybRecoveryMetric, MybRecoveryLog, MybLogRecoveryInput } from '../../types';\nimport { formatNumber, formatDate, formatRelativeTime } from '../../utils/formatters';\nimport { RECOVERY_META, IconChip } from '../_shared/visuals';\n\ntype RecoveryFilter = 'ALL' | MybRecoveryMetric;\n\ntype Accent = 'success' | 'warning' | 'error';\n\nconst RECOVERY_METRICS: MybRecoveryMetric[] = ['SLEEP', 'READINESS', 'SORENESS', 'STRESS', 'MOOD'];\n\nconst FILTER_OPTIONS: FilterOption<RecoveryFilter>[] = [\n { value: 'ALL', label: 'All' },\n ...RECOVERY_METRICS.map((m) => ({ value: m, label: RECOVERY_META[m].label })),\n];\n\n// Brand emerald accent — dominant color for the recovery area chart & endpoint.\nconst CHART_ACCENT = 'var(--color-action-primary-bg)';\n\nconst scoreAccent = (score: number): Accent =>\n score >= 70 ? 'success' : score >= 40 ? 'warning' : 'error';\n\nconst sleepAccent = (hours: number): Accent =>\n hours >= 7 ? 'success' : hours >= 6 ? 'warning' : 'error';\n\n/** Format a Date into a `datetime-local` input value (local time, minute precision). */\nfunction toDatetimeLocalValue(date: Date): string {\n const pad = (n: number): string => String(n).padStart(2, '0');\n return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}T${pad(\n date.getHours()\n )}:${pad(date.getMinutes())}`;\n}\n\ninterface RecoveryForm {\n metric: MybRecoveryMetric;\n score: string;\n sleepHours: string;\n notes: string;\n loggedAt: string;\n}\n\nconst makeEmptyForm = (metric: MybRecoveryMetric = 'READINESS'): RecoveryForm => ({\n metric,\n score: '',\n sleepHours: '',\n notes: '',\n loggedAt: toDatetimeLocalValue(new Date()),\n});\n\nexport function RecoveryPage() {\n const [filter, setFilter] = useState<RecoveryFilter>('ALL');\n const [open, setOpen] = useState(false);\n const [form, setForm] = useState<RecoveryForm>(makeEmptyForm);\n const [err, setErr] = useState<string | null>(null);\n\n // KPI feeds — independent of the active filter so the header stays stable.\n const readiness = useMybRecoveryLogs('READINESS');\n const sleep = useMybRecoveryLogs('SLEEP');\n const mood = useMybRecoveryLogs('MOOD');\n\n // Charted + tabulated metric (undefined → all metrics).\n const selectedMetric = filter === 'ALL' ? undefined : filter;\n const logs = useMybRecoveryLogs(selectedMetric);\n\n const logRecovery = useMybLogRecovery();\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 readinessRows = readiness.data?.items ?? [];\n const sleepRows = sleep.data?.items ?? [];\n const moodRows = mood.data?.items ?? [];\n const logRows = logs.data?.items ?? [];\n\n const latestReadiness = useMemo(\n () =>\n [...readinessRows].sort(\n (a, b) => new Date(b.loggedAt).getTime() - new Date(a.loggedAt).getTime()\n )[0],\n [readinessRows]\n );\n\n const latestMood = useMemo(\n () =>\n [...moodRows].sort(\n (a, b) => new Date(b.loggedAt).getTime() - new Date(a.loggedAt).getTime()\n )[0],\n [moodRows]\n );\n\n const avgSleep = useMemo(() => {\n if (sleepRows.length === 0) return null;\n const sum = sleepRows.reduce((acc, l) => acc + l.sleepHours, 0);\n return sum / sleepRows.length;\n }, [sleepRows]);\n\n const isSleepSelected = filter === 'SLEEP';\n const selectedLabel = filter === 'ALL' ? 'All metrics' : RECOVERY_META[filter].label;\n\n const chartData = useMemo(\n () =>\n [...logRows]\n .sort((a, b) => new Date(a.loggedAt).getTime() - new Date(b.loggedAt).getTime())\n .map((l) => ({\n label: formatDate(l.loggedAt),\n value: isSleepSelected ? l.sleepHours : l.score,\n })),\n [logRows, isSleepSelected]\n );\n const lastPoint = chartData.length > 0 ? chartData[chartData.length - 1] : undefined;\n\n const openLog = (metric: MybRecoveryMetric = 'READINESS') => {\n setForm(makeEmptyForm(metric));\n setErr(null);\n setOpen(true);\n };\n\n const isSleepForm = form.metric === 'SLEEP';\n\n const submit = async () => {\n setErr(null);\n if (isSleepForm) {\n if (form.sleepHours.trim() === '') {\n setErr('Sleep hours is required for sleep entries.');\n return;\n }\n const h = Number(form.sleepHours);\n if (!Number.isFinite(h) || h < 0 || h > 24) {\n setErr('Enter sleep hours between 0 and 24.');\n return;\n }\n } else {\n if (form.score.trim() === '') {\n setErr('A score is required.');\n return;\n }\n }\n if (form.score.trim() !== '') {\n const s = Number(form.score);\n if (!Number.isFinite(s) || s < 0 || s > 100) {\n setErr('Enter a score between 0 and 100.');\n return;\n }\n }\n\n const input: MybLogRecoveryInput = {\n metric: form.metric,\n score: form.score.trim() !== '' ? Number(form.score) : undefined,\n sleepHours: form.sleepHours.trim() !== '' ? Number(form.sleepHours) : undefined,\n notes: form.notes.trim() || undefined,\n loggedAt: form.loggedAt ? new Date(form.loggedAt).toISOString() : undefined,\n };\n\n try {\n await logRecovery.mutateAsync(input);\n setOpen(false);\n } catch (e) {\n setErr(e instanceof Error ? e.message : 'Failed to log recovery.');\n }\n };\n\n const columns: Column<MybRecoveryLog>[] = [\n {\n key: 'metric',\n header: 'Metric',\n sortValue: (r) => RECOVERY_META[r.metric].label,\n render: (r) => {\n const meta = RECOVERY_META[r.metric];\n const Icon = meta.icon;\n return (\n <span className=\"inline-flex items-center gap-2.5\">\n <IconChip icon={<Icon className=\"h-4 w-4\" />} tint={meta.tint} />\n <span className=\"font-medium text-text-primary\">{meta.label}</span>\n </span>\n );\n },\n },\n {\n key: 'value',\n header: 'Value',\n align: 'right',\n sortValue: (r) => (r.metric === 'SLEEP' ? r.sleepHours : r.score),\n render: (r) =>\n r.metric === 'SLEEP' ? (\n <span className=\"tabular-nums text-text-primary\">\n {formatNumber(r.sleepHours, { maximumFractionDigits: 1 })} h\n </span>\n ) : (\n <span className=\"tabular-nums text-text-primary\">{formatNumber(r.score)}</span>\n ),\n },\n {\n key: 'notes',\n header: 'Notes',\n hideOnMobile: true,\n render: (r) =>\n r.notes && r.notes.trim() ? (\n <span className=\"text-text-secondary\">{r.notes}</span>\n ) : (\n <span className=\"text-text-tertiary\">—</span>\n ),\n },\n {\n key: 'loggedAt',\n header: 'Logged',\n align: 'right',\n sortValue: (r) => new Date(r.loggedAt).getTime(),\n render: (r) => (\n <span className=\"whitespace-nowrap text-text-tertiary\">\n {formatRelativeTime(r.loggedAt)}\n </span>\n ),\n },\n ];\n\n const ReadinessIcon = RECOVERY_META.READINESS.icon;\n const SleepIcon = RECOVERY_META.SLEEP.icon;\n const MoodIcon = RECOVERY_META.MOOD.icon;\n\n return (\n <PageLayout\n title=\"Recovery\"\n description=\"Sleep, readiness, soreness, stress & mood.\"\n actions={\n <Button className=\"gap-2\" onClick={() => openLog()}>\n <Plus className=\"h-4 w-4\" />\n Log recovery\n </Button>\n }\n >\n {/* KPI grid */}\n <motion.div {...rise(0)} className=\"grid grid-cols-1 gap-4 sm:grid-cols-3\">\n <MetricsCard\n icon={\n <IconChip\n icon={<ReadinessIcon className=\"h-5 w-5\" />}\n tint={RECOVERY_META.READINESS.tint}\n />\n }\n value={latestReadiness ? formatNumber(latestReadiness.score) : '—'}\n label=\"Latest readiness\"\n accent={latestReadiness ? scoreAccent(latestReadiness.score) : undefined}\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={() => openLog('READINESS')}\n className=\"text-xs font-medium text-accent hover:underline\"\n >\n Log readiness →\n </button>\n )\n }\n />\n <MetricsCard\n icon={\n <IconChip icon={<SleepIcon className=\"h-5 w-5\" />} tint={RECOVERY_META.SLEEP.tint} />\n }\n value={\n avgSleep != null ? `${formatNumber(avgSleep, { maximumFractionDigits: 1 })} h` : '—'\n }\n label=\"Avg sleep\"\n accent={avgSleep != null ? sleepAccent(avgSleep) : undefined}\n footer={\n sleepRows.length > 0 ? (\n <p className=\"text-xs text-text-tertiary\">\n <span className=\"tabular-nums text-text-secondary\">{sleepRows.length}</span>{' '}\n {sleepRows.length === 1 ? 'night' : 'nights'} logged\n </p>\n ) : (\n <button\n type=\"button\"\n onClick={() => openLog('SLEEP')}\n className=\"text-xs font-medium text-accent hover:underline\"\n >\n Log sleep →\n </button>\n )\n }\n />\n <MetricsCard\n icon={<IconChip icon={<MoodIcon className=\"h-5 w-5\" />} tint={RECOVERY_META.MOOD.tint} />}\n value={latestMood ? formatNumber(latestMood.score) : '—'}\n label=\"Latest mood\"\n accent={latestMood ? scoreAccent(latestMood.score) : undefined}\n footer={\n latestMood ? (\n <p className=\"text-xs text-text-tertiary\">\n {formatRelativeTime(latestMood.loggedAt)}\n </p>\n ) : (\n <button\n type=\"button\"\n onClick={() => openLog('MOOD')}\n className=\"text-xs font-medium text-accent hover:underline\"\n >\n Log mood →\n </button>\n )\n }\n />\n </motion.div>\n\n {/* Metric filter */}\n <div className=\"mt-6 flex flex-wrap items-center gap-3\">\n <FilterTabs options={FILTER_OPTIONS} value={filter} onChange={setFilter} />\n </div>\n\n {/* Trend chart */}\n <Section\n title={`${selectedLabel} trend`}\n description={isSleepSelected ? 'Sleep hours over time.' : 'Recovery score over time.'}\n >\n <motion.div {...rise(0.08)}>\n <GlassCard\n treatment=\"glass\"\n className=\"rounded-2xl p-6 transition-shadow duration-300 hover:shadow-2xl\"\n >\n {logs.isLoading ? (\n <div className=\"flex h-[280px] items-center justify-center\">\n <Spinner size=\"lg\" />\n </div>\n ) : logs.isError ? (\n <ErrorView\n message=\"We couldn't load this metric's history.\"\n onRetry={() => logs.refetch()}\n />\n ) : chartData.length === 0 ? (\n <div className=\"flex h-[280px] flex-col items-center justify-center text-center\">\n <span className=\"flex size-11 items-center justify-center rounded-full bg-[var(--color-accent-soft)]\">\n <HeartPulse className=\"h-5 w-5 text-accent\" />\n </span>\n <p className=\"mt-3 text-sm font-medium text-text-primary\">Nothing to chart yet</p>\n <p className=\"mt-1 max-w-xs text-xs text-text-tertiary\">\n Log a few {selectedLabel.toLowerCase()} entries to see your trend here.\n </p>\n </div>\n ) : (\n <ResponsiveContainer width=\"100%\" height={280}>\n <AreaChart data={chartData} margin={{ top: 12, right: 16, bottom: 4, left: 4 }}>\n <defs>\n <linearGradient id=\"myb-recovery-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 vertical={false}\n stroke=\"var(--color-border-seam)\"\n strokeDasharray=\"4 4\"\n />\n <XAxis\n dataKey=\"label\"\n tickLine={false}\n axisLine={false}\n tickMargin={8}\n minTickGap={20}\n tick={{ fill: 'var(--color-text-tertiary)', fontSize: 12 }}\n />\n <YAxis\n width={44}\n tickLine={false}\n axisLine={false}\n tick={{ fill: 'var(--color-text-tertiary)', fontSize: 12 }}\n />\n <Tooltip\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 fontSize: 12,\n }}\n labelStyle={{ color: 'var(--color-text-tertiary)' }}\n cursor={{ stroke: 'var(--color-border-seam)', strokeDasharray: '4 4' }}\n />\n <Area\n type=\"monotone\"\n dataKey=\"value\"\n name={isSleepSelected ? 'Sleep hours' : 'Score'}\n stroke={CHART_ACCENT}\n strokeWidth={2.5}\n fill=\"url(#myb-recovery-fill)\"\n fillOpacity={1}\n dot={false}\n activeDot={{\n r: 4,\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 )}\n </GlassCard>\n </motion.div>\n </Section>\n\n {/* Log table */}\n <motion.div {...rise(0.16, false)}>\n <Section title=\"Recovery log\" description=\"Every recovery entry, most recent first.\">\n {logs.isLoading ? (\n <LoadingView label=\"Loading recovery logs…\" />\n ) : logs.isError ? (\n <ErrorView\n message=\"We couldn't load your recovery logs.\"\n onRetry={() => logs.refetch()}\n />\n ) : logRows.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n icon={<HeartPulse className=\"h-10 w-10\" />}\n title=\"No recovery logs yet\"\n description=\"Track your sleep, readiness, soreness, stress or mood to see how your body is recovering between sessions.\"\n action={\n <Button className=\"gap-2\" onClick={() => openLog()}>\n <Plus className=\"h-4 w-4\" />\n Log recovery\n </Button>\n }\n />\n ) : (\n <DataTable\n columns={columns}\n rows={logRows}\n rowKey={(r) => r.id}\n defaultSort={{ key: 'loggedAt', dir: 'desc' }}\n />\n )}\n </Section>\n </motion.div>\n\n {/* Log recovery dialog */}\n <ResponsiveDialog open={open} onOpenChange={setOpen}>\n <ResponsiveDialogContent className=\"sm:max-w-[480px]\">\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>Log recovery</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n Capture how your body is recovering — one metric per entry.\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n\n <div className=\"space-y-4 py-2\">\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-recovery-metric\">Metric</Label>\n <Select\n value={form.metric}\n onValueChange={(v) =>\n // Select only emits values from RECOVERY_METRICS, so `v` is a\n // MybRecoveryMetric — narrow the Radix `string` payload.\n setForm((f) => ({ ...f, metric: v as MybRecoveryMetric }))\n }\n >\n <SelectTrigger id=\"myb-recovery-metric\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {RECOVERY_METRICS.map((m) => (\n <SelectItem key={m} value={m}>\n {RECOVERY_META[m].label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n\n {isSleepForm && (\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-recovery-sleep\">Sleep hours</Label>\n <Input\n id=\"myb-recovery-sleep\"\n type=\"number\"\n min=\"0\"\n max=\"24\"\n step=\"0.1\"\n value={form.sleepHours}\n onChange={(e) => setForm((f) => ({ ...f, sleepHours: e.target.value }))}\n placeholder=\"e.g. 7.5\"\n />\n <p className=\"text-xs text-text-tertiary\">Primary measure for sleep entries.</p>\n </div>\n )}\n\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-recovery-score\">Score{isSleepForm ? ' (optional)' : ''}</Label>\n <Input\n id=\"myb-recovery-score\"\n type=\"number\"\n min=\"0\"\n max=\"100\"\n step=\"1\"\n value={form.score}\n onChange={(e) => setForm((f) => ({ ...f, score: e.target.value }))}\n placeholder=\"0–100\"\n />\n <p className=\"text-xs text-text-tertiary\">\n {isSleepForm\n ? 'Optional 0–100 sleep-quality score.'\n : 'A 0–100 rating for this metric.'}\n </p>\n </div>\n\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-recovery-at\">Logged at</Label>\n <Input\n id=\"myb-recovery-at\"\n type=\"datetime-local\"\n value={form.loggedAt}\n onChange={(e) => setForm((f) => ({ ...f, loggedAt: e.target.value }))}\n />\n </div>\n\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-recovery-notes\">Notes</Label>\n <Textarea\n id=\"myb-recovery-notes\"\n rows={3}\n value={form.notes}\n onChange={(e) => setForm((f) => ({ ...f, notes: e.target.value }))}\n placeholder=\"How are you feeling? (optional)\"\n />\n </div>\n\n {err && <p className=\"text-sm text-status-error-text\">{err}</p>}\n </div>\n\n <ResponsiveDialogFooter>\n <Button\n variant=\"outline\"\n onClick={() => setOpen(false)}\n disabled={logRecovery.isPending}\n >\n Cancel\n </Button>\n <Button onClick={submit} disabled={logRecovery.isPending}>\n {logRecovery.isPending ? 'Saving…' : 'Log recovery'}\n </Button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n </PageLayout>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AA+DA,IAAM,IAAwC;CAAC;CAAS;CAAa;CAAY;CAAU;CAAO,EAE5F,KAAiD,CACrD;CAAE,OAAO;CAAO,OAAO;CAAO,EAC9B,GAAG,EAAiB,KAAK,OAAO;CAAE,OAAO;CAAG,OAAO,EAAc,GAAG;CAAO,EAAE,CAC9E,EAGK,IAAe,kCAEf,KAAe,MACnB,KAAS,KAAK,YAAY,KAAS,KAAK,YAAY,SAEhD,KAAe,MACnB,KAAS,IAAI,YAAY,KAAS,IAAI,YAAY;AAGpD,SAAS,EAAqB,GAAoB;CAChD,IAAM,KAAO,MAAsB,OAAO,EAAE,CAAC,SAAS,GAAG,IAAI;AAC7D,QAAO,GAAG,EAAK,aAAa,CAAC,GAAG,EAAI,EAAK,UAAU,GAAG,EAAE,CAAC,GAAG,EAAI,EAAK,SAAS,CAAC,CAAC,GAAG,EACjF,EAAK,UAAU,CAChB,CAAC,GAAG,EAAI,EAAK,YAAY,CAAC;;AAW7B,IAAM,KAAiB,IAA4B,iBAA+B;CAChF;CACA,OAAO;CACP,YAAY;CACZ,OAAO;CACP,UAAU,kBAAqB,IAAI,MAAM,CAAC;CAC3C;AAED,SAAgB,IAAe;CAC7B,IAAM,CAAC,GAAQ,KAAa,EAAyB,MAAM,EACrD,CAAC,IAAM,KAAW,EAAS,GAAM,EACjC,CAAC,GAAM,KAAW,EAAuB,EAAc,EACvD,CAAC,GAAK,KAAU,EAAwB,KAAK,EAG7C,KAAY,EAAmB,YAAY,EAC3C,KAAQ,EAAmB,QAAQ,EACnC,KAAO,EAAmB,OAAO,EAIjC,IAAO,EADU,MAAW,QAAQ,KAAA,IAAY,EACP,EAEzC,IAAc,IAAmB,EAEjC,IAAU,GAAkB,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,IAAgB,GAAU,MAAM,SAAS,EAAE,EAC3C,IAAY,GAAM,MAAM,SAAS,EAAE,EACnC,IAAW,GAAK,MAAM,SAAS,EAAE,EACjC,IAAU,EAAK,MAAM,SAAS,EAAE,EAEhC,IAAkB,QAEpB,CAAC,GAAG,EAAc,CAAC,MAChB,GAAG,MAAM,IAAI,KAAK,EAAE,SAAS,CAAC,SAAS,GAAG,IAAI,KAAK,EAAE,SAAS,CAAC,SAAS,CAC1E,CAAC,IACJ,CAAC,EAAc,CAChB,EAEK,IAAa,QAEf,CAAC,GAAG,EAAS,CAAC,MACX,GAAG,MAAM,IAAI,KAAK,EAAE,SAAS,CAAC,SAAS,GAAG,IAAI,KAAK,EAAE,SAAS,CAAC,SAAS,CAC1E,CAAC,IACJ,CAAC,EAAS,CACX,EAEK,IAAW,QACX,EAAU,WAAW,IAAU,OACvB,EAAU,QAAQ,GAAK,MAAM,IAAM,EAAE,YAAY,EAAE,GAClD,EAAU,QACtB,CAAC,EAAU,CAAC,EAET,IAAkB,MAAW,SAC7B,IAAgB,MAAW,QAAQ,gBAAgB,EAAc,GAAQ,OAEzE,IAAY,QAEd,CAAC,GAAG,EAAQ,CACT,MAAM,GAAG,MAAM,IAAI,KAAK,EAAE,SAAS,CAAC,SAAS,GAAG,IAAI,KAAK,EAAE,SAAS,CAAC,SAAS,CAAC,CAC/E,KAAK,OAAO;EACX,OAAO,EAAW,EAAE,SAAS;EAC7B,OAAO,IAAkB,EAAE,aAAa,EAAE;EAC3C,EAAE,EACP,CAAC,GAAS,EAAgB,CAC3B,EACK,IAAY,EAAU,SAAS,IAAI,EAAU,EAAU,SAAS,KAAK,KAAA,GAErE,KAAW,IAA4B,gBAAgB;AAG3D,EAFA,EAAQ,EAAc,EAAO,CAAC,EAC9B,EAAO,KAAK,EACZ,EAAQ,GAAK;IAGT,IAAc,EAAK,WAAW,SAE9B,KAAS,YAAY;AAEzB,MADA,EAAO,KAAK,EACR,GAAa;AACf,OAAI,EAAK,WAAW,MAAM,KAAK,IAAI;AACjC,MAAO,6CAA6C;AACpD;;GAEF,IAAM,IAAI,OAAO,EAAK,WAAW;AACjC,OAAI,CAAC,OAAO,SAAS,EAAE,IAAI,IAAI,KAAK,IAAI,IAAI;AAC1C,MAAO,sCAAsC;AAC7C;;aAGE,EAAK,MAAM,MAAM,KAAK,IAAI;AAC5B,KAAO,uBAAuB;AAC9B;;AAGJ,MAAI,EAAK,MAAM,MAAM,KAAK,IAAI;GAC5B,IAAM,IAAI,OAAO,EAAK,MAAM;AAC5B,OAAI,CAAC,OAAO,SAAS,EAAE,IAAI,IAAI,KAAK,IAAI,KAAK;AAC3C,MAAO,mCAAmC;AAC1C;;;EAIJ,IAAM,IAA6B;GACjC,QAAQ,EAAK;GACb,OAAO,EAAK,MAAM,MAAM,KAAK,KAA0B,KAAA,IAArB,OAAO,EAAK,MAAM;GACpD,YAAY,EAAK,WAAW,MAAM,KAAK,KAA+B,KAAA,IAA1B,OAAO,EAAK,WAAW;GACnE,OAAO,EAAK,MAAM,MAAM,IAAI,KAAA;GAC5B,UAAU,EAAK,WAAW,IAAI,KAAK,EAAK,SAAS,CAAC,aAAa,GAAG,KAAA;GACnE;AAED,MAAI;AAEF,GADA,MAAM,EAAY,YAAY,EAAM,EACpC,EAAQ,GAAM;WACP,GAAG;AACV,KAAO,aAAa,QAAQ,EAAE,UAAU,0BAA0B;;IAIhE,KAAoC;EACxC;GACE,KAAK;GACL,QAAQ;GACR,YAAY,MAAM,EAAc,EAAE,QAAQ;GAC1C,SAAS,MAAM;IACb,IAAM,IAAO,EAAc,EAAE,SACvB,IAAO,EAAK;AAClB,WACE,kBAAC,QAAD;KAAM,WAAU;eAAhB,CACE,kBAAC,GAAD;MAAU,MAAM,kBAAC,GAAD,EAAM,WAAU,WAAY,CAAA;MAAE,MAAM,EAAK;MAAQ,CAAA,EACjE,kBAAC,QAAD;MAAM,WAAU;gBAAiC,EAAK;MAAa,CAAA,CAC9D;;;GAGZ;EACD;GACE,KAAK;GACL,QAAQ;GACR,OAAO;GACP,YAAY,MAAO,EAAE,WAAW,UAAU,EAAE,aAAa,EAAE;GAC3D,SAAS,MACP,EAAE,WAAW,UACX,kBAAC,QAAD;IAAM,WAAU;cAAhB,CACG,EAAa,EAAE,YAAY,EAAE,uBAAuB,GAAG,CAAC,EAAC,KACrD;QAEP,kBAAC,QAAD;IAAM,WAAU;cAAkC,EAAa,EAAE,MAAM;IAAQ,CAAA;GAEpF;EACD;GACE,KAAK;GACL,QAAQ;GACR,cAAc;GACd,SAAS,MACP,EAAE,SAAS,EAAE,MAAM,MAAM,GACvB,kBAAC,QAAD;IAAM,WAAU;cAAuB,EAAE;IAAa,CAAA,GAEtD,kBAAC,QAAD;IAAM,WAAU;cAAqB;IAAQ,CAAA;GAElD;EACD;GACE,KAAK;GACL,QAAQ;GACR,OAAO;GACP,YAAY,MAAM,IAAI,KAAK,EAAE,SAAS,CAAC,SAAS;GAChD,SAAS,MACP,kBAAC,QAAD;IAAM,WAAU;cACb,EAAmB,EAAE,SAAS;IAC1B,CAAA;GAEV;EACF,EAEK,KAAgB,EAAc,UAAU,MACxC,KAAY,EAAc,MAAM,MAChC,KAAW,EAAc,KAAK;AAEpC,QACE,kBAAC,GAAD;EACE,OAAM;EACN,aAAY;EACZ,SACE,kBAAC,GAAD;GAAQ,WAAU;GAAQ,eAAe,GAAS;aAAlD,CACE,kBAAC,GAAD,EAAM,WAAU,WAAY,CAAA,EAAA,eAErB;;YAPb;GAWE,kBAAC,EAAO,KAAR;IAAY,GAAI,EAAK,EAAE;IAAE,WAAU;cAAnC;KACE,kBAAC,GAAD;MACE,MACE,kBAAC,GAAD;OACE,MAAM,kBAAC,IAAD,EAAe,WAAU,WAAY,CAAA;OAC3C,MAAM,EAAc,UAAU;OAC9B,CAAA;MAEJ,OAAO,IAAkB,EAAa,EAAgB,MAAM,GAAG;MAC/D,OAAM;MACN,QAAQ,IAAkB,EAAY,EAAgB,MAAM,GAAG,KAAA;MAC/D,QACE,IACE,kBAAC,KAAD;OAAG,WAAU;iBACV,EAAmB,EAAgB,SAAS;OAC3C,CAAA,GAEJ,kBAAC,UAAD;OACE,MAAK;OACL,eAAe,EAAQ,YAAY;OACnC,WAAU;iBACX;OAEQ,CAAA;MAGb,CAAA;KACF,kBAAC,GAAD;MACE,MACE,kBAAC,GAAD;OAAU,MAAM,kBAAC,IAAD,EAAW,WAAU,WAAY,CAAA;OAAE,MAAM,EAAc,MAAM;OAAQ,CAAA;MAEvF,OACE,KAAY,OAAqE,MAA9D,GAAG,EAAa,GAAU,EAAE,uBAAuB,GAAG,CAAC,CAAC;MAE7E,OAAM;MACN,QAAQ,KAAY,OAA+B,KAAA,IAAxB,EAAY,EAAS;MAChD,QACE,EAAU,SAAS,IACjB,kBAAC,KAAD;OAAG,WAAU;iBAAb;QACE,kBAAC,QAAD;SAAM,WAAU;mBAAoC,EAAU;SAAc,CAAA;QAAC;QAC5E,EAAU,WAAW,IAAI,UAAU;QAAS;QAC3C;WAEJ,kBAAC,UAAD;OACE,MAAK;OACL,eAAe,EAAQ,QAAQ;OAC/B,WAAU;iBACX;OAEQ,CAAA;MAGb,CAAA;KACF,kBAAC,GAAD;MACE,MAAM,kBAAC,GAAD;OAAU,MAAM,kBAAC,IAAD,EAAU,WAAU,WAAY,CAAA;OAAE,MAAM,EAAc,KAAK;OAAQ,CAAA;MACzF,OAAO,IAAa,EAAa,EAAW,MAAM,GAAG;MACrD,OAAM;MACN,QAAQ,IAAa,EAAY,EAAW,MAAM,GAAG,KAAA;MACrD,QACE,IACE,kBAAC,KAAD;OAAG,WAAU;iBACV,EAAmB,EAAW,SAAS;OACtC,CAAA,GAEJ,kBAAC,UAAD;OACE,MAAK;OACL,eAAe,EAAQ,OAAO;OAC9B,WAAU;iBACX;OAEQ,CAAA;MAGb,CAAA;KACS;;GAGb,kBAAC,OAAD;IAAK,WAAU;cACb,kBAAC,IAAD;KAAY,SAAS;KAAgB,OAAO;KAAQ,UAAU;KAAa,CAAA;IACvE,CAAA;GAGN,kBAAC,GAAD;IACE,OAAO,GAAG,EAAc;IACxB,aAAa,IAAkB,2BAA2B;cAE1D,kBAAC,EAAO,KAAR;KAAY,GAAI,EAAK,IAAK;eACxB,kBAAC,IAAD;MACE,WAAU;MACV,WAAU;gBAET,EAAK,YACJ,kBAAC,OAAD;OAAK,WAAU;iBACb,kBAAC,IAAD,EAAS,MAAK,MAAO,CAAA;OACjB,CAAA,GACJ,EAAK,UACP,kBAAC,GAAD;OACE,SAAQ;OACR,eAAe,EAAK,SAAS;OAC7B,CAAA,GACA,EAAU,WAAW,IACvB,kBAAC,OAAD;OAAK,WAAU;iBAAf;QACE,kBAAC,QAAD;SAAM,WAAU;mBACd,kBAAC,GAAD,EAAY,WAAU,uBAAwB,CAAA;SACzC,CAAA;QACP,kBAAC,KAAD;SAAG,WAAU;mBAA6C;SAAwB,CAAA;QAClF,kBAAC,KAAD;SAAG,WAAU;mBAAb;UAAwD;UAC3C,EAAc,aAAa;UAAC;UACrC;;QACA;WAEN,kBAAC,IAAD;OAAqB,OAAM;OAAO,QAAQ;iBACxC,kBAAC,IAAD;QAAW,MAAM;QAAW,QAAQ;SAAE,KAAK;SAAI,OAAO;SAAI,QAAQ;SAAG,MAAM;SAAG;kBAA9E;SACE,kBAAC,QAAD,EAAA,UACE,kBAAC,kBAAD;UAAgB,IAAG;UAAoB,IAAG;UAAI,IAAG;UAAI,IAAG;UAAI,IAAG;oBAA/D;WACE,kBAAC,QAAD;YAAM,QAAO;YAAK,WAAW;YAAc,aAAa;YAAQ,CAAA;WAChE,kBAAC,QAAD;YAAM,QAAO;YAAM,WAAW;YAAc,aAAa;YAAQ,CAAA;WACjE,kBAAC,QAAD;YAAM,QAAO;YAAO,WAAW;YAAc,aAAa;YAAK,CAAA;WAChD;aACZ,CAAA;SACP,kBAAC,IAAD;UACE,UAAU;UACV,QAAO;UACP,iBAAgB;UAChB,CAAA;SACF,kBAAC,IAAD;UACE,SAAQ;UACR,UAAU;UACV,UAAU;UACV,YAAY;UACZ,YAAY;UACZ,MAAM;WAAE,MAAM;WAA8B,UAAU;WAAI;UAC1D,CAAA;SACF,kBAAC,IAAD;UACE,OAAO;UACP,UAAU;UACV,UAAU;UACV,MAAM;WAAE,MAAM;WAA8B,UAAU;WAAI;UAC1D,CAAA;SACF,kBAAC,GAAD;UACE,cAAc;WACZ,YAAY;WACZ,QAAQ;WACR,cAAc;WACd,OAAO;WACP,UAAU;WACX;UACD,YAAY,EAAE,OAAO,8BAA8B;UACnD,QAAQ;WAAE,QAAQ;WAA4B,iBAAiB;WAAO;UACtE,CAAA;SACF,kBAAC,IAAD;UACE,MAAK;UACL,SAAQ;UACR,MAAM,IAAkB,gBAAgB;UACxC,QAAQ;UACR,aAAa;UACb,MAAK;UACL,aAAa;UACb,KAAK;UACL,WAAW;WACT,GAAG;WACH,aAAa;WACb,QAAQ;WACR,MAAM;WACP;UACD,mBAAmB,CAAC;UACpB,mBAAmB;UACnB,CAAA;SACD,KACC,kBAAC,GAAD;UACE,GAAG,EAAU;UACb,GAAG,EAAU;UACb,GAAG;UACH,SAAA;UACA,YAAW;UACX,MAAM;UACN,aAAa;UACb,QAAO;UACP,CAAA;SAEH,KACC,kBAAC,GAAD;UACE,GAAG,EAAU;UACb,GAAG,EAAU;UACb,GAAG;UACH,SAAA;UACA,YAAW;UACX,MAAM;UACN,QAAO;UACP,aAAa;UACb,CAAA;SAEM;;OACQ,CAAA;MAEd,CAAA;KACD,CAAA;IACL,CAAA;GAGV,kBAAC,EAAO,KAAR;IAAY,GAAI,EAAK,KAAM,GAAM;cAC/B,kBAAC,GAAD;KAAS,OAAM;KAAe,aAAY;eACvC,EAAK,YACJ,kBAAC,IAAD,EAAa,OAAM,0BAA2B,CAAA,GAC5C,EAAK,UACP,kBAAC,GAAD;MACE,SAAQ;MACR,eAAe,EAAK,SAAS;MAC7B,CAAA,GACA,EAAQ,WAAW,IACrB,kBAAC,IAAD;MACE,cAAa;MACb,MAAM,kBAAC,GAAD,EAAY,WAAU,aAAc,CAAA;MAC1C,OAAM;MACN,aAAY;MACZ,QACE,kBAAC,GAAD;OAAQ,WAAU;OAAQ,eAAe,GAAS;iBAAlD,CACE,kBAAC,GAAD,EAAM,WAAU,WAAY,CAAA,EAAA,eAErB;;MAEX,CAAA,GAEF,kBAAC,IAAD;MACW;MACT,MAAM;MACN,SAAS,MAAM,EAAE;MACjB,aAAa;OAAE,KAAK;OAAY,KAAK;OAAQ;MAC7C,CAAA;KAEI,CAAA;IACC,CAAA;GAGb,kBAAC,IAAD;IAAwB;IAAM,cAAc;cAC1C,kBAAC,IAAD;KAAyB,WAAU;eAAnC;MACE,kBAAC,IAAD,EAAA,UAAA,CACE,kBAAC,IAAD,EAAA,UAAuB,gBAAoC,CAAA,EAC3D,kBAAC,GAAD,EAAA,UAA6B,+DAEC,CAAA,CACP,EAAA,CAAA;MAEzB,kBAAC,OAAD;OAAK,WAAU;iBAAf;QACE,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,GAAD;UAAO,SAAQ;oBAAsB;UAAc,CAAA,EACnD,kBAAC,IAAD;UACE,OAAO,EAAK;UACZ,gBAAgB,MAGd,GAAS,OAAO;WAAE,GAAG;WAAG,QAAQ;WAAwB,EAAE;oBAL9D,CAQE,kBAAC,IAAD;WAAe,IAAG;qBAChB,kBAAC,IAAD,EAAe,CAAA;WACD,CAAA,EAChB,kBAAC,IAAD,EAAA,UACG,EAAiB,KAAK,MACrB,kBAAC,IAAD;WAAoB,OAAO;qBACxB,EAAc,GAAG;WACP,EAFI,EAEJ,CACb,EACY,CAAA,CACT;YACL;;QAEL,KACC,kBAAC,OAAD;SAAK,WAAU;mBAAf;UACE,kBAAC,GAAD;WAAO,SAAQ;qBAAqB;WAAmB,CAAA;UACvD,kBAAC,GAAD;WACE,IAAG;WACH,MAAK;WACL,KAAI;WACJ,KAAI;WACJ,MAAK;WACL,OAAO,EAAK;WACZ,WAAW,MAAM,GAAS,OAAO;YAAE,GAAG;YAAG,YAAY,EAAE,OAAO;YAAO,EAAE;WACvE,aAAY;WACZ,CAAA;UACF,kBAAC,KAAD;WAAG,WAAU;qBAA6B;WAAsC,CAAA;UAC5E;;QAGR,kBAAC,OAAD;SAAK,WAAU;mBAAf;UACE,kBAAC,GAAD;WAAO,SAAQ;qBAAf,CAAoC,SAAM,IAAc,gBAAgB,GAAW;;UACnF,kBAAC,GAAD;WACE,IAAG;WACH,MAAK;WACL,KAAI;WACJ,KAAI;WACJ,MAAK;WACL,OAAO,EAAK;WACZ,WAAW,MAAM,GAAS,OAAO;YAAE,GAAG;YAAG,OAAO,EAAE,OAAO;YAAO,EAAE;WAClE,aAAY;WACZ,CAAA;UACF,kBAAC,KAAD;WAAG,WAAU;qBACV,IACG,wCACA;WACF,CAAA;UACA;;QAEN,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,GAAD;UAAO,SAAQ;oBAAkB;UAAiB,CAAA,EAClD,kBAAC,GAAD;UACE,IAAG;UACH,MAAK;UACL,OAAO,EAAK;UACZ,WAAW,MAAM,GAAS,OAAO;WAAE,GAAG;WAAG,UAAU,EAAE,OAAO;WAAO,EAAE;UACrE,CAAA,CACE;;QAEN,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,GAAD;UAAO,SAAQ;oBAAqB;UAAa,CAAA,EACjD,kBAAC,IAAD;UACE,IAAG;UACH,MAAM;UACN,OAAO,EAAK;UACZ,WAAW,MAAM,GAAS,OAAO;WAAE,GAAG;WAAG,OAAO,EAAE,OAAO;WAAO,EAAE;UAClE,aAAY;UACZ,CAAA,CACE;;QAEL,KAAO,kBAAC,KAAD;SAAG,WAAU;mBAAkC;SAAQ,CAAA;QAC3D;;MAEN,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD;OACE,SAAQ;OACR,eAAe,EAAQ,GAAM;OAC7B,UAAU,EAAY;iBACvB;OAEQ,CAAA,EACT,kBAAC,GAAD;OAAQ,SAAS;OAAQ,UAAU,EAAY;iBAC5C,EAAY,YAAY,YAAY;OAC9B,CAAA,CACc,EAAA,CAAA;MACD;;IACT,CAAA;GACR"}
|
|
1
|
+
{"version":3,"file":"RecoveryPage.js","names":[],"sources":["../../../../src/mybodyshield/pages/recovery/RecoveryPage.tsx"],"sourcesContent":["/**\n * Recovery — sleep, readiness, soreness, stress & mood tracking.\n * @module mybodyshield/pages/recovery\n *\n * Reads the real `mybListRecoveryLogs` array endpoint (no pagination). KPIs pull\n * the readiness / sleep / mood metrics independently so they stay stable while\n * the FilterTabs drive the charted + tabulated metric. New entries go through\n * `mybLogRecovery`. No mock data — everything is derived from the hooks.\n */\n\nimport { useMemo, useState } from 'react';\nimport { Plus, HeartPulse } from 'lucide-react';\nimport {\n ResponsiveContainer,\n AreaChart,\n Area,\n XAxis,\n YAxis,\n CartesianGrid,\n Tooltip,\n ReferenceDot,\n} from 'recharts';\nimport { motion, useReducedMotion, type MotionProps } from 'framer-motion';\nimport {\n Button,\n Input,\n Label,\n Textarea,\n Select,\n SelectTrigger,\n SelectContent,\n SelectItem,\n SelectValue,\n GlassCard,\n Spinner,\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 LoadingView,\n ErrorView,\n Section,\n EmptyState,\n type Column,\n type FilterOption,\n} from '../../components';\nimport { useMybRecoveryLogs, useMybLogRecovery, useTr } from '../../hooks';\nimport type { MybRecoveryMetric, MybRecoveryLog, MybLogRecoveryInput } from '../../types';\nimport { formatNumber, formatDate, formatRelativeTime } from '../../utils/formatters';\nimport { RECOVERY_META, IconChip } from '../_shared/visuals';\n\ntype RecoveryFilter = 'ALL' | MybRecoveryMetric;\n\ntype Accent = 'success' | 'warning' | 'error';\n\nconst RECOVERY_METRICS: MybRecoveryMetric[] = ['SLEEP', 'READINESS', 'SORENESS', 'STRESS', 'MOOD'];\n\n// Brand emerald accent — dominant color for the recovery area chart & endpoint.\nconst CHART_ACCENT = 'var(--color-action-primary-bg)';\n\nconst scoreAccent = (score: number): Accent =>\n score >= 70 ? 'success' : score >= 40 ? 'warning' : 'error';\n\nconst sleepAccent = (hours: number): Accent =>\n hours >= 7 ? 'success' : hours >= 6 ? 'warning' : 'error';\n\n/** Format a Date into a `datetime-local` input value (local time, minute precision). */\nfunction toDatetimeLocalValue(date: Date): string {\n const pad = (n: number): string => String(n).padStart(2, '0');\n return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}T${pad(\n date.getHours()\n )}:${pad(date.getMinutes())}`;\n}\n\ninterface RecoveryForm {\n metric: MybRecoveryMetric;\n score: string;\n sleepHours: string;\n notes: string;\n loggedAt: string;\n}\n\nconst makeEmptyForm = (metric: MybRecoveryMetric = 'READINESS'): RecoveryForm => ({\n metric,\n score: '',\n sleepHours: '',\n notes: '',\n loggedAt: toDatetimeLocalValue(new Date()),\n});\n\n/** Localised label for a recovery metric enum value. */\nfunction recoveryMetricLabel(t: ReturnType<typeof useTr>, metric: MybRecoveryMetric): string {\n const keyRoot = 'mybodyshield.pages.recovery.metricNames';\n switch (metric) {\n case 'SLEEP':\n return t(`${keyRoot}.sleep`, 'Sleep');\n case 'READINESS':\n return t(`${keyRoot}.readiness`, 'Readiness');\n case 'SORENESS':\n return t(`${keyRoot}.soreness`, 'Soreness');\n case 'STRESS':\n return t(`${keyRoot}.stress`, 'Stress');\n case 'MOOD':\n return t(`${keyRoot}.mood`, 'Mood');\n }\n}\n\nexport function RecoveryPage() {\n const t = useTr();\n const [filter, setFilter] = useState<RecoveryFilter>('ALL');\n const [open, setOpen] = useState(false);\n const [form, setForm] = useState<RecoveryForm>(makeEmptyForm);\n const [err, setErr] = useState<string | null>(null);\n\n // KPI feeds — independent of the active filter so the header stays stable.\n const readiness = useMybRecoveryLogs('READINESS');\n const sleep = useMybRecoveryLogs('SLEEP');\n const mood = useMybRecoveryLogs('MOOD');\n\n // Charted + tabulated metric (undefined → all metrics).\n const selectedMetric = filter === 'ALL' ? undefined : filter;\n const logs = useMybRecoveryLogs(selectedMetric);\n\n const logRecovery = useMybLogRecovery();\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 readinessRows = readiness.data?.items ?? [];\n const sleepRows = sleep.data?.items ?? [];\n const moodRows = mood.data?.items ?? [];\n const logRows = logs.data?.items ?? [];\n\n const latestReadiness = useMemo(\n () =>\n [...readinessRows].sort(\n (a, b) => new Date(b.loggedAt).getTime() - new Date(a.loggedAt).getTime()\n )[0],\n [readinessRows]\n );\n\n const latestMood = useMemo(\n () =>\n [...moodRows].sort(\n (a, b) => new Date(b.loggedAt).getTime() - new Date(a.loggedAt).getTime()\n )[0],\n [moodRows]\n );\n\n const avgSleep = useMemo(() => {\n if (sleepRows.length === 0) return null;\n const sum = sleepRows.reduce((acc, l) => acc + l.sleepHours, 0);\n return sum / sleepRows.length;\n }, [sleepRows]);\n\n const isSleepSelected = filter === 'SLEEP';\n const selectedLabel =\n filter === 'ALL'\n ? t('mybodyshield.pages.recovery.allMetrics', 'All metrics')\n : recoveryMetricLabel(t, filter);\n\n const filterOptions: FilterOption<RecoveryFilter>[] = [\n { value: 'ALL', label: t('mybodyshield.common.all', 'All') },\n ...RECOVERY_METRICS.map((metric) => ({\n value: metric,\n label: recoveryMetricLabel(t, metric),\n })),\n ];\n\n const chartData = useMemo(\n () =>\n [...logRows]\n .sort((a, b) => new Date(a.loggedAt).getTime() - new Date(b.loggedAt).getTime())\n .map((l) => ({\n label: formatDate(l.loggedAt),\n value: isSleepSelected ? l.sleepHours : l.score,\n })),\n [logRows, isSleepSelected]\n );\n const lastPoint = chartData.length > 0 ? chartData[chartData.length - 1] : undefined;\n\n const openLog = (metric: MybRecoveryMetric = 'READINESS') => {\n setForm(makeEmptyForm(metric));\n setErr(null);\n setOpen(true);\n };\n\n const isSleepForm = form.metric === 'SLEEP';\n\n const submit = async () => {\n setErr(null);\n if (isSleepForm) {\n if (form.sleepHours.trim() === '') {\n setErr(\n t(\n 'mybodyshield.pages.recovery.sleepHoursRequired',\n 'Sleep hours is required for sleep entries.'\n )\n );\n return;\n }\n const h = Number(form.sleepHours);\n if (!Number.isFinite(h) || h < 0 || h > 24) {\n setErr(\n t('mybodyshield.pages.recovery.sleepHoursRange', 'Enter sleep hours between 0 and 24.')\n );\n return;\n }\n } else {\n if (form.score.trim() === '') {\n setErr(t('mybodyshield.pages.recovery.scoreRequired', 'A score is required.'));\n return;\n }\n }\n if (form.score.trim() !== '') {\n const s = Number(form.score);\n if (!Number.isFinite(s) || s < 0 || s > 100) {\n setErr(t('mybodyshield.pages.recovery.scoreRange', 'Enter a score between 0 and 100.'));\n return;\n }\n }\n\n const input: MybLogRecoveryInput = {\n metric: form.metric,\n score: form.score.trim() !== '' ? Number(form.score) : undefined,\n sleepHours: form.sleepHours.trim() !== '' ? Number(form.sleepHours) : undefined,\n notes: form.notes.trim() || undefined,\n loggedAt: form.loggedAt ? new Date(form.loggedAt).toISOString() : undefined,\n };\n\n try {\n await logRecovery.mutateAsync(input);\n setOpen(false);\n } catch (e) {\n setErr(\n e instanceof Error\n ? e.message\n : t('mybodyshield.pages.recovery.failedToLog', 'Failed to log recovery.')\n );\n }\n };\n\n const columns: Column<MybRecoveryLog>[] = [\n {\n key: 'metric',\n header: t('mybodyshield.pages.recovery.metricColumn', 'Metric'),\n sortValue: (r) => recoveryMetricLabel(t, r.metric),\n render: (r) => {\n const meta = RECOVERY_META[r.metric];\n const Icon = meta.icon;\n return (\n <span className=\"inline-flex items-center gap-2.5\">\n <IconChip icon={<Icon className=\"h-4 w-4\" />} tint={meta.tint} />\n <span className=\"font-medium text-text-primary\">\n {recoveryMetricLabel(t, r.metric)}\n </span>\n </span>\n );\n },\n },\n {\n key: 'value',\n header: t('mybodyshield.pages.recovery.valueColumn', 'Value'),\n align: 'right',\n sortValue: (r) => (r.metric === 'SLEEP' ? r.sleepHours : r.score),\n render: (r) =>\n r.metric === 'SLEEP' ? (\n <span className=\"tabular-nums text-text-primary\">\n {formatNumber(r.sleepHours, { maximumFractionDigits: 1 })} h\n </span>\n ) : (\n <span className=\"tabular-nums text-text-primary\">{formatNumber(r.score)}</span>\n ),\n },\n {\n key: 'notes',\n header: t('mybodyshield.pages.recovery.notesColumn', 'Notes'),\n hideOnMobile: true,\n render: (r) =>\n r.notes && r.notes.trim() ? (\n <span className=\"text-text-secondary\">{r.notes}</span>\n ) : (\n <span className=\"text-text-tertiary\">—</span>\n ),\n },\n {\n key: 'loggedAt',\n header: t('mybodyshield.pages.recovery.loggedColumn', 'Logged'),\n align: 'right',\n sortValue: (r) => new Date(r.loggedAt).getTime(),\n render: (r) => (\n <span className=\"whitespace-nowrap text-text-tertiary\">\n {formatRelativeTime(r.loggedAt)}\n </span>\n ),\n },\n ];\n\n const ReadinessIcon = RECOVERY_META.READINESS.icon;\n const SleepIcon = RECOVERY_META.SLEEP.icon;\n const MoodIcon = RECOVERY_META.MOOD.icon;\n\n return (\n <PageLayout\n title={t('mybodyshield.pages.recovery.title', 'Recovery')}\n description={t(\n 'mybodyshield.pages.recovery.description',\n 'Sleep, readiness, soreness, stress & mood.'\n )}\n actions={\n <Button className=\"gap-2\" onClick={() => openLog()}>\n <Plus className=\"h-4 w-4\" />\n {t('mybodyshield.pages.recovery.logRecoveryButton', 'Log recovery')}\n </Button>\n }\n >\n {/* KPI grid */}\n <motion.div {...rise(0)} className=\"grid grid-cols-1 gap-4 sm:grid-cols-3\">\n <MetricsCard\n icon={\n <IconChip\n icon={<ReadinessIcon className=\"h-5 w-5\" />}\n tint={RECOVERY_META.READINESS.tint}\n />\n }\n value={latestReadiness ? formatNumber(latestReadiness.score) : '—'}\n label={t('mybodyshield.pages.recovery.latestReadiness', 'Latest readiness')}\n accent={latestReadiness ? scoreAccent(latestReadiness.score) : undefined}\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={() => openLog('READINESS')}\n className=\"text-xs font-medium text-accent hover:underline\"\n >\n {t('mybodyshield.pages.recovery.logReadiness', 'Log readiness →')}\n </button>\n )\n }\n />\n <MetricsCard\n icon={\n <IconChip icon={<SleepIcon className=\"h-5 w-5\" />} tint={RECOVERY_META.SLEEP.tint} />\n }\n value={\n avgSleep != null ? `${formatNumber(avgSleep, { maximumFractionDigits: 1 })} h` : '—'\n }\n label={t('mybodyshield.pages.recovery.avgSleep', 'Avg sleep')}\n accent={avgSleep != null ? sleepAccent(avgSleep) : undefined}\n footer={\n sleepRows.length > 0 ? (\n <p className=\"text-xs text-text-tertiary\">\n <span className=\"tabular-nums text-text-secondary\">{sleepRows.length}</span>{' '}\n {sleepRows.length === 1\n ? t('mybodyshield.pages.recovery.nightsLogged', '{{count}} night logged', {\n count: sleepRows.length,\n })\n : t('mybodyshield.pages.recovery.nightsLoggedPlural', '{{count}} nights logged', {\n count: sleepRows.length,\n })}\n </p>\n ) : (\n <button\n type=\"button\"\n onClick={() => openLog('SLEEP')}\n className=\"text-xs font-medium text-accent hover:underline\"\n >\n {t('mybodyshield.pages.recovery.logSleep', 'Log sleep →')}\n </button>\n )\n }\n />\n <MetricsCard\n icon={<IconChip icon={<MoodIcon className=\"h-5 w-5\" />} tint={RECOVERY_META.MOOD.tint} />}\n value={latestMood ? formatNumber(latestMood.score) : '—'}\n label={t('mybodyshield.pages.recovery.latestMood', 'Latest mood')}\n accent={latestMood ? scoreAccent(latestMood.score) : undefined}\n footer={\n latestMood ? (\n <p className=\"text-xs text-text-tertiary\">\n {formatRelativeTime(latestMood.loggedAt)}\n </p>\n ) : (\n <button\n type=\"button\"\n onClick={() => openLog('MOOD')}\n className=\"text-xs font-medium text-accent hover:underline\"\n >\n {t('mybodyshield.pages.recovery.logMood', 'Log mood →')}\n </button>\n )\n }\n />\n </motion.div>\n\n {/* Metric filter */}\n <div className=\"mt-6 flex flex-wrap items-center gap-3\">\n <FilterTabs options={filterOptions} value={filter} onChange={setFilter} />\n </div>\n\n {/* Trend chart */}\n <Section\n title={t('mybodyshield.pages.recovery.trendTitle', '{{metric}} trend', {\n metric: selectedLabel,\n })}\n description={\n isSleepSelected\n ? t('mybodyshield.pages.recovery.sleepTrendDescription', 'Sleep hours over time.')\n : t('mybodyshield.pages.recovery.scoreTrendDescription', 'Recovery score over time.')\n }\n >\n <motion.div {...rise(0.08)}>\n <GlassCard\n treatment=\"glass\"\n className=\"rounded-2xl p-6 transition-shadow duration-300 hover:shadow-2xl\"\n >\n {logs.isLoading ? (\n <div className=\"flex h-[280px] items-center justify-center\">\n <Spinner size=\"lg\" />\n </div>\n ) : logs.isError ? (\n <ErrorView\n message={t(\n 'mybodyshield.pages.recovery.chartError',\n \"We couldn't load this metric's history.\"\n )}\n onRetry={() => logs.refetch()}\n />\n ) : chartData.length === 0 ? (\n <div className=\"flex h-[280px] flex-col items-center justify-center text-center\">\n <span className=\"flex size-11 items-center justify-center rounded-full bg-[var(--color-accent-soft)]\">\n <HeartPulse className=\"h-5 w-5 text-accent\" />\n </span>\n <p className=\"mt-3 text-sm font-medium text-text-primary\">\n {t('mybodyshield.pages.recovery.emptyChartTitle', 'Nothing to chart yet')}\n </p>\n <p className=\"mt-1 max-w-xs text-xs text-text-tertiary\">\n {t(\n 'mybodyshield.pages.recovery.emptyChartDescription',\n 'Log a few {{metric}} entries to see your trend here.',\n { metric: selectedLabel.toLowerCase() }\n )}\n </p>\n </div>\n ) : (\n <ResponsiveContainer width=\"100%\" height={280}>\n <AreaChart data={chartData} margin={{ top: 12, right: 16, bottom: 4, left: 4 }}>\n <defs>\n <linearGradient id=\"myb-recovery-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 vertical={false}\n stroke=\"var(--color-border-seam)\"\n strokeDasharray=\"4 4\"\n />\n <XAxis\n dataKey=\"label\"\n tickLine={false}\n axisLine={false}\n tickMargin={8}\n minTickGap={20}\n tick={{ fill: 'var(--color-text-tertiary)', fontSize: 12 }}\n />\n <YAxis\n width={44}\n tickLine={false}\n axisLine={false}\n tick={{ fill: 'var(--color-text-tertiary)', fontSize: 12 }}\n />\n <Tooltip\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 fontSize: 12,\n }}\n labelStyle={{ color: 'var(--color-text-tertiary)' }}\n cursor={{ stroke: 'var(--color-border-seam)', strokeDasharray: '4 4' }}\n />\n <Area\n type=\"monotone\"\n dataKey=\"value\"\n name={\n isSleepSelected\n ? t('mybodyshield.pages.recovery.sleepHours', 'Sleep hours')\n : t('mybodyshield.pages.recovery.score', 'Score')\n }\n stroke={CHART_ACCENT}\n strokeWidth={2.5}\n fill=\"url(#myb-recovery-fill)\"\n fillOpacity={1}\n dot={false}\n activeDot={{\n r: 4,\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 )}\n </GlassCard>\n </motion.div>\n </Section>\n\n {/* Log table */}\n <motion.div {...rise(0.16, false)}>\n <Section\n title={t('mybodyshield.pages.recovery.recoveryLog', 'Recovery log')}\n description={t(\n 'mybodyshield.pages.recovery.recoveryLogDescription',\n 'Every recovery entry, most recent first.'\n )}\n >\n {logs.isLoading ? (\n <LoadingView\n label={t('mybodyshield.pages.recovery.loading', 'Loading recovery logs…')}\n />\n ) : logs.isError ? (\n <ErrorView\n message={t(\n 'mybodyshield.pages.recovery.error',\n \"We couldn't load your recovery logs.\"\n )}\n onRetry={() => logs.refetch()}\n />\n ) : logRows.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n icon={<HeartPulse className=\"h-10 w-10\" />}\n title={t('mybodyshield.pages.recovery.emptyTitle', 'No recovery logs yet')}\n description={t(\n 'mybodyshield.pages.recovery.emptyDescription',\n 'Track your sleep, readiness, soreness, stress or mood to see how your body is recovering between sessions.'\n )}\n action={\n <Button className=\"gap-2\" onClick={() => openLog()}>\n <Plus className=\"h-4 w-4\" />\n {t('mybodyshield.pages.recovery.logRecoveryButton', 'Log recovery')}\n </Button>\n }\n />\n ) : (\n <DataTable\n columns={columns}\n rows={logRows}\n rowKey={(r) => r.id}\n defaultSort={{ key: 'loggedAt', dir: 'desc' }}\n />\n )}\n </Section>\n </motion.div>\n\n {/* Log recovery dialog */}\n <ResponsiveDialog open={open} onOpenChange={setOpen}>\n <ResponsiveDialogContent className=\"sm:max-w-[480px]\">\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>\n {t('mybodyshield.pages.recovery.dialogTitle', 'Log recovery')}\n </ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n {t(\n 'mybodyshield.pages.recovery.dialogDescription',\n 'Capture how your body is recovering — one metric per entry.'\n )}\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n\n <div className=\"space-y-4 py-2\">\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-recovery-metric\">\n {t('mybodyshield.pages.recovery.metric', 'Metric')}\n </Label>\n <Select\n value={form.metric}\n onValueChange={(v) =>\n // Select only emits values from RECOVERY_METRICS, so `v` is a\n // MybRecoveryMetric — narrow the Radix `string` payload.\n setForm((f) => ({ ...f, metric: v as MybRecoveryMetric }))\n }\n >\n <SelectTrigger id=\"myb-recovery-metric\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {RECOVERY_METRICS.map((metric) => (\n <SelectItem key={metric} value={metric}>\n {recoveryMetricLabel(t, metric)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n\n {isSleepForm && (\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-recovery-sleep\">\n {t('mybodyshield.pages.recovery.sleepHours', 'Sleep hours')}\n </Label>\n <Input\n id=\"myb-recovery-sleep\"\n type=\"number\"\n min=\"0\"\n max=\"24\"\n step=\"0.1\"\n value={form.sleepHours}\n onChange={(e) => setForm((f) => ({ ...f, sleepHours: e.target.value }))}\n placeholder={t('mybodyshield.pages.recovery.sleepHoursPlaceholder', 'e.g. 7.5')}\n />\n <p className=\"text-xs text-text-tertiary\">\n {t(\n 'mybodyshield.pages.recovery.sleepHoursHint',\n 'Primary measure for sleep entries.'\n )}\n </p>\n </div>\n )}\n\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-recovery-score\">\n {isSleepForm\n ? t('mybodyshield.pages.recovery.scoreOptional', 'Score (optional)')\n : t('mybodyshield.pages.recovery.score', 'Score')}\n </Label>\n <Input\n id=\"myb-recovery-score\"\n type=\"number\"\n min=\"0\"\n max=\"100\"\n step=\"1\"\n value={form.score}\n onChange={(e) => setForm((f) => ({ ...f, score: e.target.value }))}\n placeholder={t('mybodyshield.pages.recovery.scorePlaceholder', '0–100')}\n />\n <p className=\"text-xs text-text-tertiary\">\n {isSleepForm\n ? t(\n 'mybodyshield.pages.recovery.sleepScoreHint',\n 'Optional 0–100 sleep-quality score.'\n )\n : t('mybodyshield.pages.recovery.scoreHint', 'A 0–100 rating for this metric.')}\n </p>\n </div>\n\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-recovery-at\">\n {t('mybodyshield.pages.recovery.loggedAt', 'Logged at')}\n </Label>\n <Input\n id=\"myb-recovery-at\"\n type=\"datetime-local\"\n value={form.loggedAt}\n onChange={(e) => setForm((f) => ({ ...f, loggedAt: e.target.value }))}\n />\n </div>\n\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-recovery-notes\">{t('mybodyshield.common.notes', 'Notes')}</Label>\n <Textarea\n id=\"myb-recovery-notes\"\n rows={3}\n value={form.notes}\n onChange={(e) => setForm((f) => ({ ...f, notes: e.target.value }))}\n placeholder={t(\n 'mybodyshield.pages.recovery.notesPlaceholder',\n 'How are you feeling? (optional)'\n )}\n />\n </div>\n\n {err && <p className=\"text-sm text-status-error-text\">{err}</p>}\n </div>\n\n <ResponsiveDialogFooter>\n <Button\n variant=\"outline\"\n onClick={() => setOpen(false)}\n disabled={logRecovery.isPending}\n >\n {t('mybodyshield.common.cancel', 'Cancel')}\n </Button>\n <Button onClick={submit} disabled={logRecovery.isPending}>\n {logRecovery.isPending\n ? t('mybodyshield.common.saving', 'Saving…')\n : t('mybodyshield.pages.recovery.logRecoveryButton', 'Log recovery')}\n </Button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SessionDetailPage.d.ts","sourceRoot":"","sources":["../../../../src/mybodyshield/pages/train/SessionDetailPage.tsx"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;
|
|
1
|
+
{"version":3,"file":"SessionDetailPage.d.ts","sourceRoot":"","sources":["../../../../src/mybodyshield/pages/train/SessionDetailPage.tsx"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAyLH,wBAAgB,iBAAiB,gCA2mChC"}
|