@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":"ProgramsPage.js","names":[],"sources":["../../../../src/mybodyshield/pages/programs/ProgramsPage.tsx"],"sourcesContent":["/**\n * Programs — training program library (Training pillar).\n * @module mybodyshield/pages/programs\n *\n * Lists the member's structured training blocks (programs), filterable by\n * lifecycle status and goal type, with a create dialog. Every program links to\n * its detail page. All data comes from the real `mybListPrograms` /\n * `mybCreateProgram` operations — no mock data.\n */\n\nimport { useMemo, useState, type FC, type ReactNode } from 'react';\nimport { motion, useReducedMotion } from 'framer-motion';\nimport { Plus, Dumbbell, FilePenLine, Layers, Clock } from 'lucide-react';\nimport {\n Button,\n Input,\n Label,\n Textarea,\n Select,\n SelectTrigger,\n SelectContent,\n SelectItem,\n SelectValue,\n GlassCard,\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n ResponsiveDialogDescription,\n ResponsiveDialogFooter,\n} from '@burdenoff/fe-libs/ui';\nimport {\n PageLayout,\n MetricsCard,\n StatusBadge,\n EmptyState,\n LoadingView,\n ErrorView,\n FilterTabs,\n Pagination,\n type FilterOption,\n} from '../../components';\nimport { IconChip, GOAL_TYPE_META } from '../_shared/visuals';\nimport { useMyBodyShield } from '../../context';\nimport { useMybPrograms, useMybCreateProgram, useOffsetPager } from '../../hooks';\nimport { formatEnumLabel, formatRelativeTime, formatNumber } from '../../utils/formatters';\nimport type {\n MybProgram,\n MybProgramStatus,\n MybGoalType,\n MybFitnessLevel,\n MybProgramPhase,\n MybCreateProgramInput,\n} from '../../types';\n\nconst PAGE_SIZE = 24;\n\nconst STATUS_FILTERS = ['ALL', 'DRAFT', 'ACTIVE', 'COMPLETED', 'ARCHIVED'] as const;\ntype StatusFilter = (typeof STATUS_FILTERS)[number];\n\nconst GOAL_TYPES: readonly MybGoalType[] = [\n 'WEIGHT_LOSS',\n 'MUSCLE_GAIN',\n 'STRENGTH',\n 'ENDURANCE',\n 'MOBILITY',\n 'GENERAL_FITNESS',\n];\nconst LEVELS: readonly MybFitnessLevel[] = ['BEGINNER', 'INTERMEDIATE', 'ADVANCED', 'ELITE'];\nconst PHASES: readonly MybProgramPhase[] = ['BUILD', 'PEAK', 'DELOAD', 'MAINTENANCE'];\n\ntype GoalFilter = MybGoalType | 'ALL';\n\ninterface ProgramForm {\n name: string;\n description: string;\n goalType: MybGoalType;\n level: MybFitnessLevel;\n phase: MybProgramPhase;\n weeks: string;\n daysPerWeek: string;\n startDate: string;\n}\n\nconst EMPTY_FORM: ProgramForm = {\n name: '',\n description: '',\n goalType: 'GENERAL_FITNESS',\n level: 'BEGINNER',\n phase: 'BUILD',\n weeks: '',\n daysPerWeek: '',\n startDate: '',\n};\n\n/** Parse a numeric text field → number | null (null when blank / invalid). */\nconst toNum = (raw: string): number | null => {\n const t = raw.trim();\n if (t === '') return null;\n const n = Number(t);\n return Number.isFinite(n) ? n : null;\n};\n\nconst REVEAL_EASE: [number, number, number, number] = [0.22, 1, 0.36, 1];\n\n/** Staggered card entrance + gentle hover lift (reduced-motion safe). */\nconst Reveal: FC<{\n index?: number;\n hover?: boolean;\n className?: string;\n children: ReactNode;\n}> = ({ index = 0, hover = false, className, children }) => {\n const reduced = useReducedMotion();\n return (\n <motion.div\n className={className}\n initial={reduced ? false : { opacity: 0, y: 12 }}\n animate={{ opacity: 1, y: 0 }}\n transition={\n reduced\n ? { duration: 0 }\n : { duration: 0.35, delay: Math.min(index * 0.05, 0.45), ease: REVEAL_EASE }\n }\n whileHover={reduced || !hover ? undefined : { y: -4 }}\n >\n {children}\n </motion.div>\n );\n};\n\n/** Token-safe generic enum <Select>. */\nfunction EnumSelect<T extends string>({\n id,\n value,\n onChange,\n options,\n}: {\n id?: string;\n value: T;\n onChange: (value: T) => void;\n options: readonly T[];\n}) {\n return (\n <Select value={value} onValueChange={(v) => onChange(v as T)}>\n <SelectTrigger id={id}>\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {options.map((opt) => (\n <SelectItem key={opt} value={opt}>\n {formatEnumLabel(opt)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n );\n}\n\nfunction ProgramCard({\n program,\n index,\n onOpen,\n}: {\n program: MybProgram;\n index: number;\n onOpen: (id: string) => void;\n}) {\n const goalMeta = GOAL_TYPE_META[program.goalType];\n const GoalIcon = goalMeta.icon;\n const metaLine = [program.phase, program.level, program.goalType]\n .map((value) => formatEnumLabel(value))\n .join(' · ');\n return (\n <Reveal index={index} hover className=\"h-full\">\n <button\n type=\"button\"\n onClick={() => onOpen(program.id)}\n className=\"block h-full w-full text-left\"\n >\n <GlassCard\n treatment=\"glass\"\n className=\"flex h-full flex-col p-5 transition-shadow hover:shadow-[var(--shadow-pop-hover)]\"\n >\n <div className=\"flex items-start justify-between gap-3\">\n <div className=\"flex min-w-0 items-start gap-3\">\n <IconChip icon={<GoalIcon className=\"h-4 w-4\" />} tint={goalMeta.tint} />\n <div className=\"min-w-0\">\n <p className=\"truncate font-semibold text-text-primary\">{program.name}</p>\n <p className=\"mt-0.5 truncate text-xs text-text-tertiary\">{metaLine}</p>\n </div>\n </div>\n <StatusBadge status={program.status} />\n </div>\n\n {program.description && (\n <p className=\"mt-3 line-clamp-2 text-sm text-text-secondary\">{program.description}</p>\n )}\n\n <div className=\"mt-3 inline-flex items-center gap-1.5 text-sm text-text-secondary\">\n <Clock className=\"h-4 w-4 shrink-0 text-action-primary-bg\" />\n <span className=\"tabular-nums\">\n {formatNumber(program.weeks)} wk × {formatNumber(program.daysPerWeek)} days\n </span>\n </div>\n\n {program.tags.length > 0 && (\n <div className=\"mt-3 flex flex-wrap gap-1.5\">\n {program.tags.slice(0, 4).map((tag) => (\n <span\n key={tag}\n className=\"inline-flex items-center rounded-full bg-[var(--color-accent-soft)] px-2 py-0.5 text-xs font-medium text-text-secondary\"\n >\n #{tag}\n </span>\n ))}\n </div>\n )}\n\n <div className=\"mt-auto pt-4 text-xs text-text-tertiary\">\n Updated {formatRelativeTime(program.updatedAt)}\n </div>\n </GlassCard>\n </button>\n </Reveal>\n );\n}\n\nexport function ProgramsPage() {\n const { navigateTo } = useMyBodyShield();\n const pager = useOffsetPager(PAGE_SIZE);\n\n const [statusFilter, setStatusFilter] = useState<StatusFilter>('ALL');\n const [goalFilter, setGoalFilter] = useState<GoalFilter>('ALL');\n\n const [createOpen, setCreateOpen] = useState(false);\n const [form, setForm] = useState<ProgramForm>(EMPTY_FORM);\n const [err, setErr] = useState<string | null>(null);\n\n const create = useMybCreateProgram();\n\n // Aggregate summary (unfiltered, capped) for the KPI row + filter counts.\n const summary = useMybPrograms({ pagination: { limit: 100 } });\n const summaryRows = useMemo(() => summary.data?.items ?? [], [summary.data]);\n\n const counts = useMemo(() => {\n const by = (s: MybProgramStatus) => summaryRows.filter((p) => p.status === s).length;\n return {\n ALL: summaryRows.length,\n DRAFT: by('DRAFT'),\n ACTIVE: by('ACTIVE'),\n COMPLETED: by('COMPLETED'),\n ARCHIVED: by('ARCHIVED'),\n };\n }, [summaryRows]);\n\n const list = useMybPrograms({\n status: statusFilter === 'ALL' ? undefined : statusFilter,\n pagination: { limit: PAGE_SIZE, offset: pager.offset },\n });\n\n const rawRows = list.data?.items ?? [];\n const rows = goalFilter === 'ALL' ? rawRows : rawRows.filter((p) => p.goalType === goalFilter);\n\n const changeStatus = (next: StatusFilter) => {\n setStatusFilter(next);\n pager.reset();\n };\n const changeGoal = (next: GoalFilter) => {\n setGoalFilter(next);\n pager.reset();\n };\n\n const openCreate = () => {\n setForm(EMPTY_FORM);\n setErr(null);\n setCreateOpen(true);\n };\n\n const submit = async () => {\n if (!form.name.trim()) {\n setErr('Program name is required.');\n return;\n }\n const input: MybCreateProgramInput = {\n name: form.name.trim(),\n description: form.description.trim() || null,\n goalType: form.goalType,\n level: form.level,\n phase: form.phase,\n weeks: toNum(form.weeks),\n daysPerWeek: toNum(form.daysPerWeek),\n startDate: form.startDate ? new Date(form.startDate).toISOString() : null,\n };\n try {\n const created = await create.mutateAsync(input);\n setCreateOpen(false);\n navigateTo(`programs/${created.id}`);\n } catch (e) {\n setErr(e instanceof Error ? e.message : 'Failed to create program.');\n }\n };\n\n const statusOptions: FilterOption<StatusFilter>[] = STATUS_FILTERS.map((s) => ({\n value: s,\n label: s === 'ALL' ? 'All' : formatEnumLabel(s),\n count: counts[s],\n }));\n\n return (\n <PageLayout\n title=\"Programs\"\n description=\"Structured training blocks — build, peak, deload & maintain.\"\n actions={\n <Button onClick={openCreate}>\n <Plus className=\"mr-1 h-4 w-4\" /> New program\n </Button>\n }\n >\n {/* KPI row */}\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-3\">\n <Reveal index={0} className=\"h-full\">\n <MetricsCard\n className=\"h-full\"\n icon={<Dumbbell className=\"h-5 w-5\" />}\n value={formatNumber(counts.ACTIVE)}\n label=\"Active programs\"\n accent=\"success\"\n />\n </Reveal>\n <Reveal index={1} className=\"h-full\">\n <MetricsCard\n className=\"h-full\"\n icon={<FilePenLine className=\"h-5 w-5\" />}\n value={formatNumber(counts.DRAFT)}\n label=\"Draft programs\"\n />\n </Reveal>\n <Reveal index={2} className=\"h-full\">\n <MetricsCard\n className=\"h-full\"\n icon={<Layers className=\"h-5 w-5\" />}\n value={formatNumber(counts.ALL)}\n label=\"Total programs\"\n />\n </Reveal>\n </div>\n\n {/* Filters */}\n <div className=\"mt-6 flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between\">\n <FilterTabs options={statusOptions} value={statusFilter} onChange={changeStatus} />\n <div className=\"flex items-center gap-2\">\n <span className=\"text-sm text-text-tertiary\">Goal</span>\n <div className=\"w-48\">\n <Select value={goalFilter} onValueChange={(v) => changeGoal(v as GoalFilter)}>\n <SelectTrigger>\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"ALL\">All goals</SelectItem>\n {GOAL_TYPES.map((g) => (\n <SelectItem key={g} value={g}>\n {formatEnumLabel(g)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n </div>\n </div>\n\n {/* List */}\n <div className=\"mt-6\">\n {list.isLoading ? (\n <LoadingView label=\"Loading programs…\" />\n ) : list.isError ? (\n <ErrorView\n message=\"We couldn't load your programs.\"\n onRetry={() => void list.refetch()}\n />\n ) : rawRows.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n icon={<Dumbbell className=\"h-10 w-10\" />}\n title={statusFilter === 'ALL' ? 'No programs yet' : 'No programs in this status'}\n description=\"Create a structured training program to plan your workouts week by week.\"\n action={\n <Button onClick={openCreate}>\n <Plus className=\"mr-1 h-4 w-4\" /> New program\n </Button>\n }\n />\n ) : rows.length === 0 ? (\n <div className=\"rounded-xl border border-dashed border-border-seam py-10 text-center text-sm text-text-secondary\">\n No programs on this page match{' '}\n <span className=\"font-medium text-text-primary\">{formatEnumLabel(goalFilter)}</span>.\n </div>\n ) : (\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n {rows.map((program, i) => (\n <ProgramCard\n key={program.id}\n program={program}\n index={i}\n onOpen={(id) => navigateTo(`programs/${id}`)}\n />\n ))}\n </div>\n )}\n\n {!list.isLoading && !list.isError && rawRows.length > 0 && (\n <Pagination\n page={pager.page}\n pageSize={PAGE_SIZE}\n itemCount={rawRows.length}\n hasMore={list.data?.pageInfo?.hasMore ?? false}\n onPageChange={(t) => pager.onPageChange(t)}\n />\n )}\n </div>\n\n {/* Create dialog */}\n <ResponsiveDialog open={createOpen} onOpenChange={setCreateOpen}>\n <ResponsiveDialogContent className=\"sm:max-w-lg\">\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>New program</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n Define a structured training block. You can add workouts &amp; exercises after\n creating it.\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n\n <div className=\"space-y-4 py-2\">\n {err && (\n <p className=\"rounded-lg bg-status-error-bg-subtle px-3 py-2 text-sm text-status-error-text\">\n {err}\n </p>\n )}\n\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-prog-name\">Name</Label>\n <Input\n id=\"myb-prog-name\"\n value={form.name}\n onChange={(e) => setForm({ ...form, name: e.target.value })}\n placeholder=\"e.g. Hypertrophy Block A\"\n />\n </div>\n\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-prog-desc\">Description</Label>\n <Textarea\n id=\"myb-prog-desc\"\n rows={3}\n value={form.description}\n onChange={(e) => setForm({ ...form, description: e.target.value })}\n placeholder=\"Focus, progression scheme, notes…\"\n />\n </div>\n\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-3\">\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-prog-goal\">Goal type</Label>\n <EnumSelect\n id=\"myb-prog-goal\"\n value={form.goalType}\n onChange={(v) => setForm({ ...form, goalType: v })}\n options={GOAL_TYPES}\n />\n </div>\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-prog-level\">Level</Label>\n <EnumSelect\n id=\"myb-prog-level\"\n value={form.level}\n onChange={(v) => setForm({ ...form, level: v })}\n options={LEVELS}\n />\n </div>\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-prog-phase\">Phase</Label>\n <EnumSelect\n id=\"myb-prog-phase\"\n value={form.phase}\n onChange={(v) => setForm({ ...form, phase: v })}\n options={PHASES}\n />\n </div>\n </div>\n\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-3\">\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-prog-weeks\">Weeks</Label>\n <Input\n id=\"myb-prog-weeks\"\n type=\"number\"\n min={1}\n value={form.weeks}\n onChange={(e) => setForm({ ...form, weeks: e.target.value })}\n placeholder=\"8\"\n />\n </div>\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-prog-days\">Days / week</Label>\n <Input\n id=\"myb-prog-days\"\n type=\"number\"\n min={1}\n max={7}\n value={form.daysPerWeek}\n onChange={(e) => setForm({ ...form, daysPerWeek: e.target.value })}\n placeholder=\"4\"\n />\n </div>\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-prog-start\">Start date</Label>\n <Input\n id=\"myb-prog-start\"\n type=\"date\"\n value={form.startDate}\n onChange={(e) => setForm({ ...form, startDate: e.target.value })}\n />\n </div>\n </div>\n </div>\n\n <ResponsiveDialogFooter>\n <Button\n variant=\"outline\"\n onClick={() => setCreateOpen(false)}\n disabled={create.isPending}\n >\n Cancel\n </Button>\n <Button onClick={() => void submit()} disabled={create.isPending}>\n {create.isPending ? 'Creating…' : 'Create program'}\n </Button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"ProgramsPage.js","names":[],"sources":["../../../../src/mybodyshield/pages/programs/ProgramsPage.tsx"],"sourcesContent":["/**\n * Programs — training program library (Training pillar).\n * @module mybodyshield/pages/programs\n *\n * Lists the member's structured training blocks (programs), filterable by\n * lifecycle status and goal type, with a create dialog. Every program links to\n * its detail page. All data comes from the real `mybListPrograms` /\n * `mybCreateProgram` operations — no mock data.\n */\n\nimport { useMemo, useState, type FC, type ReactNode } from 'react';\nimport { motion, useReducedMotion } from 'framer-motion';\nimport { Plus, Dumbbell, FilePenLine, Layers, Clock } from 'lucide-react';\nimport {\n Button,\n Input,\n Label,\n Textarea,\n Select,\n SelectTrigger,\n SelectContent,\n SelectItem,\n SelectValue,\n GlassCard,\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n ResponsiveDialogDescription,\n ResponsiveDialogFooter,\n} from '@burdenoff/fe-libs/ui';\nimport {\n PageLayout,\n MetricsCard,\n StatusBadge,\n EmptyState,\n LoadingView,\n ErrorView,\n FilterTabs,\n Pagination,\n type FilterOption,\n} from '../../components';\nimport { IconChip, GOAL_TYPE_META } from '../_shared/visuals';\nimport { useMyBodyShield } from '../../context';\nimport {\n useMybPrograms,\n useMybCreateProgram,\n useOffsetPager,\n useMyBodyShieldAnalytics,\n} from '../../hooks';\nimport { formatEnumLabel, formatRelativeTime, formatNumber } from '../../utils/formatters';\nimport type {\n MybProgram,\n MybProgramStatus,\n MybGoalType,\n MybFitnessLevel,\n MybProgramPhase,\n MybCreateProgramInput,\n} from '../../types';\n\nconst PAGE_SIZE = 24;\n\nconst STATUS_FILTERS = ['ALL', 'DRAFT', 'ACTIVE', 'COMPLETED', 'ARCHIVED'] as const;\ntype StatusFilter = (typeof STATUS_FILTERS)[number];\n\nconst GOAL_TYPES: readonly MybGoalType[] = [\n 'WEIGHT_LOSS',\n 'MUSCLE_GAIN',\n 'STRENGTH',\n 'ENDURANCE',\n 'MOBILITY',\n 'GENERAL_FITNESS',\n];\nconst LEVELS: readonly MybFitnessLevel[] = ['BEGINNER', 'INTERMEDIATE', 'ADVANCED', 'ELITE'];\nconst PHASES: readonly MybProgramPhase[] = ['BUILD', 'PEAK', 'DELOAD', 'MAINTENANCE'];\n\ntype GoalFilter = MybGoalType | 'ALL';\n\ninterface ProgramForm {\n name: string;\n description: string;\n goalType: MybGoalType;\n level: MybFitnessLevel;\n phase: MybProgramPhase;\n weeks: string;\n daysPerWeek: string;\n startDate: string;\n}\n\nconst EMPTY_FORM: ProgramForm = {\n name: '',\n description: '',\n goalType: 'GENERAL_FITNESS',\n level: 'BEGINNER',\n phase: 'BUILD',\n weeks: '',\n daysPerWeek: '',\n startDate: '',\n};\n\n/** Parse a numeric text field → number | null (null when blank / invalid). */\nconst toNum = (raw: string): number | null => {\n const t = raw.trim();\n if (t === '') return null;\n const n = Number(t);\n return Number.isFinite(n) ? n : null;\n};\n\nconst REVEAL_EASE: [number, number, number, number] = [0.22, 1, 0.36, 1];\n\n/** Staggered card entrance + gentle hover lift (reduced-motion safe). */\nconst Reveal: FC<{\n index?: number;\n hover?: boolean;\n className?: string;\n children: ReactNode;\n}> = ({ index = 0, hover = false, className, children }) => {\n const reduced = useReducedMotion();\n return (\n <motion.div\n className={className}\n initial={reduced ? false : { opacity: 0, y: 12 }}\n animate={{ opacity: 1, y: 0 }}\n transition={\n reduced\n ? { duration: 0 }\n : { duration: 0.35, delay: Math.min(index * 0.05, 0.45), ease: REVEAL_EASE }\n }\n whileHover={reduced || !hover ? undefined : { y: -4 }}\n >\n {children}\n </motion.div>\n );\n};\n\n/** Token-safe generic enum <Select>. */\nfunction EnumSelect<T extends string>({\n id,\n value,\n onChange,\n options,\n}: {\n id?: string;\n value: T;\n onChange: (value: T) => void;\n options: readonly T[];\n}) {\n return (\n <Select value={value} onValueChange={(v) => onChange(v as T)}>\n <SelectTrigger id={id}>\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {options.map((opt) => (\n <SelectItem key={opt} value={opt}>\n {formatEnumLabel(opt)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n );\n}\n\nfunction ProgramCard({\n program,\n index,\n onOpen,\n}: {\n program: MybProgram;\n index: number;\n onOpen: (id: string) => void;\n}) {\n const goalMeta = GOAL_TYPE_META[program.goalType];\n const GoalIcon = goalMeta.icon;\n const metaLine = [program.phase, program.level, program.goalType]\n .map((value) => formatEnumLabel(value))\n .join(' · ');\n return (\n <Reveal index={index} hover className=\"h-full\">\n <button\n type=\"button\"\n onClick={() => onOpen(program.id)}\n className=\"block h-full w-full text-left\"\n >\n <GlassCard\n treatment=\"glass\"\n className=\"flex h-full flex-col p-5 transition-shadow hover:shadow-[var(--shadow-pop-hover)]\"\n >\n <div className=\"flex items-start justify-between gap-3\">\n <div className=\"flex min-w-0 items-start gap-3\">\n <IconChip icon={<GoalIcon className=\"h-4 w-4\" />} tint={goalMeta.tint} />\n <div className=\"min-w-0\">\n <p className=\"truncate font-semibold text-text-primary\">{program.name}</p>\n <p className=\"mt-0.5 truncate text-xs text-text-tertiary\">{metaLine}</p>\n </div>\n </div>\n <StatusBadge status={program.status} />\n </div>\n\n {program.description && (\n <p className=\"mt-3 line-clamp-2 text-sm text-text-secondary\">{program.description}</p>\n )}\n\n <div className=\"mt-3 inline-flex items-center gap-1.5 text-sm text-text-secondary\">\n <Clock className=\"h-4 w-4 shrink-0 text-action-primary-bg\" />\n <span className=\"tabular-nums\">\n {formatNumber(program.weeks)} wk × {formatNumber(program.daysPerWeek)} days\n </span>\n </div>\n\n {program.tags.length > 0 && (\n <div className=\"mt-3 flex flex-wrap gap-1.5\">\n {program.tags.slice(0, 4).map((tag) => (\n <span\n key={tag}\n className=\"inline-flex items-center rounded-full bg-[var(--color-accent-soft)] px-2 py-0.5 text-xs font-medium text-text-secondary\"\n >\n #{tag}\n </span>\n ))}\n </div>\n )}\n\n <div className=\"mt-auto pt-4 text-xs text-text-tertiary\">\n Updated {formatRelativeTime(program.updatedAt)}\n </div>\n </GlassCard>\n </button>\n </Reveal>\n );\n}\n\nexport function ProgramsPage() {\n const { navigateTo, workspaceId } = useMyBodyShield();\n const pager = useOffsetPager(PAGE_SIZE);\n const analytics = useMyBodyShieldAnalytics();\n\n const [statusFilter, setStatusFilter] = useState<StatusFilter>('ALL');\n const [goalFilter, setGoalFilter] = useState<GoalFilter>('ALL');\n\n const [createOpen, setCreateOpen] = useState(false);\n const [form, setForm] = useState<ProgramForm>(EMPTY_FORM);\n const [err, setErr] = useState<string | null>(null);\n\n const create = useMybCreateProgram();\n\n // Aggregate summary (unfiltered, capped) for the KPI row + filter counts.\n const summary = useMybPrograms({ pagination: { limit: 100 } });\n const summaryRows = useMemo(() => summary.data?.items ?? [], [summary.data]);\n\n const counts = useMemo(() => {\n const by = (s: MybProgramStatus) => summaryRows.filter((p) => p.status === s).length;\n return {\n ALL: summaryRows.length,\n DRAFT: by('DRAFT'),\n ACTIVE: by('ACTIVE'),\n COMPLETED: by('COMPLETED'),\n ARCHIVED: by('ARCHIVED'),\n };\n }, [summaryRows]);\n\n const list = useMybPrograms({\n status: statusFilter === 'ALL' ? undefined : statusFilter,\n pagination: { limit: PAGE_SIZE, offset: pager.offset },\n });\n\n const rawRows = list.data?.items ?? [];\n const rows = goalFilter === 'ALL' ? rawRows : rawRows.filter((p) => p.goalType === goalFilter);\n\n const changeStatus = (next: StatusFilter) => {\n setStatusFilter(next);\n pager.reset();\n };\n const changeGoal = (next: GoalFilter) => {\n setGoalFilter(next);\n pager.reset();\n };\n\n const openCreate = () => {\n setForm(EMPTY_FORM);\n setErr(null);\n setCreateOpen(true);\n analytics.emitIntent('program', 'create', { workspaceId });\n };\n\n const submit = async () => {\n if (!form.name.trim()) {\n setErr('Program name is required.');\n return;\n }\n const input: MybCreateProgramInput = {\n name: form.name.trim(),\n description: form.description.trim() || null,\n goalType: form.goalType,\n level: form.level,\n phase: form.phase,\n weeks: toNum(form.weeks),\n daysPerWeek: toNum(form.daysPerWeek),\n startDate: form.startDate ? new Date(form.startDate).toISOString() : null,\n };\n try {\n const created = await create.mutateAsync(input);\n analytics.emitFact('program', 'created', { workspaceId, entityId: created.id });\n setCreateOpen(false);\n navigateTo(`programs/${created.id}`);\n } catch (e) {\n setErr(e instanceof Error ? e.message : 'Failed to create program.');\n }\n };\n\n const statusOptions: FilterOption<StatusFilter>[] = STATUS_FILTERS.map((s) => ({\n value: s,\n label: s === 'ALL' ? 'All' : formatEnumLabel(s),\n count: counts[s],\n }));\n\n return (\n <PageLayout\n title=\"Programs\"\n description=\"Structured training blocks — build, peak, deload & maintain.\"\n actions={\n <Button onClick={openCreate}>\n <Plus className=\"mr-1 h-4 w-4\" /> New program\n </Button>\n }\n >\n {/* KPI row */}\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-3\">\n <Reveal index={0} className=\"h-full\">\n <MetricsCard\n className=\"h-full\"\n icon={<Dumbbell className=\"h-5 w-5\" />}\n value={formatNumber(counts.ACTIVE)}\n label=\"Active programs\"\n accent=\"success\"\n />\n </Reveal>\n <Reveal index={1} className=\"h-full\">\n <MetricsCard\n className=\"h-full\"\n icon={<FilePenLine className=\"h-5 w-5\" />}\n value={formatNumber(counts.DRAFT)}\n label=\"Draft programs\"\n />\n </Reveal>\n <Reveal index={2} className=\"h-full\">\n <MetricsCard\n className=\"h-full\"\n icon={<Layers className=\"h-5 w-5\" />}\n value={formatNumber(counts.ALL)}\n label=\"Total programs\"\n />\n </Reveal>\n </div>\n\n {/* Filters */}\n <div className=\"mt-6 flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between\">\n <FilterTabs options={statusOptions} value={statusFilter} onChange={changeStatus} />\n <div className=\"flex items-center gap-2\">\n <span className=\"text-sm text-text-tertiary\">Goal</span>\n <div className=\"w-48\">\n <Select value={goalFilter} onValueChange={(v) => changeGoal(v as GoalFilter)}>\n <SelectTrigger>\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"ALL\">All goals</SelectItem>\n {GOAL_TYPES.map((g) => (\n <SelectItem key={g} value={g}>\n {formatEnumLabel(g)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n </div>\n </div>\n\n {/* List */}\n <div className=\"mt-6\">\n {list.isLoading ? (\n <LoadingView label=\"Loading programs…\" />\n ) : list.isError ? (\n <ErrorView\n message=\"We couldn't load your programs.\"\n onRetry={() => void list.refetch()}\n />\n ) : rawRows.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n icon={<Dumbbell className=\"h-10 w-10\" />}\n title={statusFilter === 'ALL' ? 'No programs yet' : 'No programs in this status'}\n description=\"Create a structured training program to plan your workouts week by week.\"\n action={\n <Button onClick={openCreate}>\n <Plus className=\"mr-1 h-4 w-4\" /> New program\n </Button>\n }\n />\n ) : rows.length === 0 ? (\n <div className=\"rounded-xl border border-dashed border-border-seam py-10 text-center text-sm text-text-secondary\">\n No programs on this page match{' '}\n <span className=\"font-medium text-text-primary\">{formatEnumLabel(goalFilter)}</span>.\n </div>\n ) : (\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n {rows.map((program, i) => (\n <ProgramCard\n key={program.id}\n program={program}\n index={i}\n onOpen={(id) => navigateTo(`programs/${id}`)}\n />\n ))}\n </div>\n )}\n\n {!list.isLoading && !list.isError && rawRows.length > 0 && (\n <Pagination\n page={pager.page}\n pageSize={PAGE_SIZE}\n itemCount={rawRows.length}\n hasMore={list.data?.pageInfo?.hasMore ?? false}\n onPageChange={(t) => pager.onPageChange(t)}\n />\n )}\n </div>\n\n {/* Create dialog */}\n <ResponsiveDialog open={createOpen} onOpenChange={setCreateOpen}>\n <ResponsiveDialogContent className=\"sm:max-w-lg\">\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>New program</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n Define a structured training block. You can add workouts &amp; exercises after\n creating it.\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n\n <div className=\"space-y-4 py-2\">\n {err && (\n <p className=\"rounded-lg bg-status-error-bg-subtle px-3 py-2 text-sm text-status-error-text\">\n {err}\n </p>\n )}\n\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-prog-name\">Name</Label>\n <Input\n id=\"myb-prog-name\"\n value={form.name}\n onChange={(e) => setForm({ ...form, name: e.target.value })}\n placeholder=\"e.g. Hypertrophy Block A\"\n />\n </div>\n\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-prog-desc\">Description</Label>\n <Textarea\n id=\"myb-prog-desc\"\n rows={3}\n value={form.description}\n onChange={(e) => setForm({ ...form, description: e.target.value })}\n placeholder=\"Focus, progression scheme, notes…\"\n />\n </div>\n\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-3\">\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-prog-goal\">Goal type</Label>\n <EnumSelect\n id=\"myb-prog-goal\"\n value={form.goalType}\n onChange={(v) => setForm({ ...form, goalType: v })}\n options={GOAL_TYPES}\n />\n </div>\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-prog-level\">Level</Label>\n <EnumSelect\n id=\"myb-prog-level\"\n value={form.level}\n onChange={(v) => setForm({ ...form, level: v })}\n options={LEVELS}\n />\n </div>\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-prog-phase\">Phase</Label>\n <EnumSelect\n id=\"myb-prog-phase\"\n value={form.phase}\n onChange={(v) => setForm({ ...form, phase: v })}\n options={PHASES}\n />\n </div>\n </div>\n\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-3\">\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-prog-weeks\">Weeks</Label>\n <Input\n id=\"myb-prog-weeks\"\n type=\"number\"\n min={1}\n value={form.weeks}\n onChange={(e) => setForm({ ...form, weeks: e.target.value })}\n placeholder=\"8\"\n />\n </div>\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-prog-days\">Days / week</Label>\n <Input\n id=\"myb-prog-days\"\n type=\"number\"\n min={1}\n max={7}\n value={form.daysPerWeek}\n onChange={(e) => setForm({ ...form, daysPerWeek: e.target.value })}\n placeholder=\"4\"\n />\n </div>\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-prog-start\">Start date</Label>\n <Input\n id=\"myb-prog-start\"\n type=\"date\"\n value={form.startDate}\n onChange={(e) => setForm({ ...form, startDate: e.target.value })}\n />\n </div>\n </div>\n </div>\n\n <ResponsiveDialogFooter>\n <Button\n variant=\"outline\"\n onClick={() => setCreateOpen(false)}\n disabled={create.isPending}\n >\n Cancel\n </Button>\n <Button onClick={() => void submit()} disabled={create.isPending}>\n {create.isPending ? 'Creating…' : 'Create program'}\n </Button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Progress — body metrics over time.
3
+ * @module mybodyshield/pages/progress
4
+ *
5
+ * A single-metric time-series view over the real `mybListBodyMetrics`
6
+ * endpoint: pick a body-metric type (weight, body-fat, measurements…), see the
7
+ * latest value + trend vs the previous reading, a token-styled line chart of the
8
+ * history window, aggregate stats, and an offset-paginated log table. New
9
+ * readings are recorded through `mybRecordBodyMetric`. No mock data — everything
10
+ * derives from the hooks.
11
+ */
12
+ export declare function ProgressPage(): import("react").JSX.Element;
13
+ //# sourceMappingURL=ProgressPage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ProgressPage.d.ts","sourceRoot":"","sources":["../../../../src/mybodyshield/pages/progress/ProgressPage.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAuHH,wBAAgB,YAAY,gCAmhB3B"}