@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.
Files changed (90) hide show
  1. package/dist/mybodyshield/MyBodyShieldRoot.d.ts.map +1 -1
  2. package/dist/mybodyshield/MyBodyShieldRoot.js +54 -56
  3. package/dist/mybodyshield/MyBodyShieldRoot.js.map +1 -1
  4. package/dist/mybodyshield/MyBodyShieldRoutes.d.ts.map +1 -1
  5. package/dist/mybodyshield/MyBodyShieldRoutes.js +121 -62
  6. package/dist/mybodyshield/MyBodyShieldRoutes.js.map +1 -1
  7. package/dist/mybodyshield/components/EvidenceUpload.d.ts.map +1 -1
  8. package/dist/mybodyshield/components/EvidenceUpload.js +46 -45
  9. package/dist/mybodyshield/components/EvidenceUpload.js.map +1 -1
  10. package/dist/mybodyshield/components/FileLink.d.ts.map +1 -1
  11. package/dist/mybodyshield/components/FileLink.js +27 -18
  12. package/dist/mybodyshield/components/FileLink.js.map +1 -1
  13. package/dist/mybodyshield/components/MybDiscussion.d.ts +1 -1
  14. package/dist/mybodyshield/components/MybDiscussion.d.ts.map +1 -1
  15. package/dist/mybodyshield/components/MybDiscussion.js +59 -58
  16. package/dist/mybodyshield/components/MybDiscussion.js.map +1 -1
  17. package/dist/mybodyshield/components/MybTags.d.ts +1 -1
  18. package/dist/mybodyshield/components/MybTags.d.ts.map +1 -1
  19. package/dist/mybodyshield/components/MybTags.js +83 -74
  20. package/dist/mybodyshield/components/MybTags.js.map +1 -1
  21. package/dist/mybodyshield/components/PageDisabled.d.ts.map +1 -1
  22. package/dist/mybodyshield/components/PageDisabled.js +16 -11
  23. package/dist/mybodyshield/components/PageDisabled.js.map +1 -1
  24. package/dist/mybodyshield/components/Pagination.d.ts.map +1 -1
  25. package/dist/mybodyshield/components/Pagination.js +36 -27
  26. package/dist/mybodyshield/components/Pagination.js.map +1 -1
  27. package/dist/mybodyshield/components/StateViews.d.ts.map +1 -1
  28. package/dist/mybodyshield/components/StateViews.js +42 -34
  29. package/dist/mybodyshield/components/StateViews.js.map +1 -1
  30. package/dist/mybodyshield/components/UserSearchSelect.d.ts.map +1 -1
  31. package/dist/mybodyshield/components/UserSearchSelect.js +66 -68
  32. package/dist/mybodyshield/components/UserSearchSelect.js.map +1 -1
  33. package/dist/mybodyshield/hooks/index.d.ts +1 -0
  34. package/dist/mybodyshield/hooks/index.d.ts.map +1 -1
  35. package/dist/mybodyshield/hooks/index.js +1 -0
  36. package/dist/mybodyshield/hooks/useTr.d.ts +5 -0
  37. package/dist/mybodyshield/hooks/useTr.d.ts.map +1 -0
  38. package/dist/mybodyshield/hooks/useTr.js +12 -0
  39. package/dist/mybodyshield/hooks/useTr.js.map +1 -0
  40. package/dist/mybodyshield/pages/DashboardPage.d.ts.map +1 -1
  41. package/dist/mybodyshield/pages/DashboardPage.js +157 -136
  42. package/dist/mybodyshield/pages/DashboardPage.js.map +1 -1
  43. package/dist/mybodyshield/pages/SettingsPage.d.ts.map +1 -1
  44. package/dist/mybodyshield/pages/SettingsPage.js +50 -59
  45. package/dist/mybodyshield/pages/SettingsPage.js.map +1 -1
  46. package/dist/mybodyshield/pages/_shared/visuals.d.ts +2 -1
  47. package/dist/mybodyshield/pages/_shared/visuals.d.ts.map +1 -1
  48. package/dist/mybodyshield/pages/_shared/visuals.js +91 -81
  49. package/dist/mybodyshield/pages/_shared/visuals.js.map +1 -1
  50. package/dist/mybodyshield/pages/ai/AiControlsPage.d.ts.map +1 -1
  51. package/dist/mybodyshield/pages/ai/AiControlsPage.js +255 -266
  52. package/dist/mybodyshield/pages/ai/AiControlsPage.js.map +1 -1
  53. package/dist/mybodyshield/pages/coaching/CoachingPage.d.ts.map +1 -1
  54. package/dist/mybodyshield/pages/coaching/CoachingPage.js +321 -301
  55. package/dist/mybodyshield/pages/coaching/CoachingPage.js.map +1 -1
  56. package/dist/mybodyshield/pages/goals/GoalsPage.d.ts.map +1 -1
  57. package/dist/mybodyshield/pages/goals/GoalsPage.js +247 -251
  58. package/dist/mybodyshield/pages/goals/GoalsPage.js.map +1 -1
  59. package/dist/mybodyshield/pages/insights/InsightsCharts.d.ts.map +1 -1
  60. package/dist/mybodyshield/pages/insights/InsightsCharts.js +216 -213
  61. package/dist/mybodyshield/pages/insights/InsightsCharts.js.map +1 -1
  62. package/dist/mybodyshield/pages/insights/InsightsPage.d.ts.map +1 -1
  63. package/dist/mybodyshield/pages/insights/InsightsPage.js +290 -284
  64. package/dist/mybodyshield/pages/insights/InsightsPage.js.map +1 -1
  65. package/dist/mybodyshield/pages/nutrition/NutritionPage.d.ts.map +1 -1
  66. package/dist/mybodyshield/pages/nutrition/NutritionPage.js +575 -529
  67. package/dist/mybodyshield/pages/nutrition/NutritionPage.js.map +1 -1
  68. package/dist/mybodyshield/pages/programs/ProgramDetailPage.d.ts.map +1 -1
  69. package/dist/mybodyshield/pages/programs/ProgramDetailPage.js +333 -275
  70. package/dist/mybodyshield/pages/programs/ProgramDetailPage.js.map +1 -1
  71. package/dist/mybodyshield/pages/programs/ProgramsPage.d.ts.map +1 -1
  72. package/dist/mybodyshield/pages/programs/ProgramsPage.js +258 -253
  73. package/dist/mybodyshield/pages/programs/ProgramsPage.js.map +1 -1
  74. package/dist/mybodyshield/pages/progress/ProgressPage.d.ts.map +1 -1
  75. package/dist/mybodyshield/pages/progress/ProgressPage.js +271 -254
  76. package/dist/mybodyshield/pages/progress/ProgressPage.js.map +1 -1
  77. package/dist/mybodyshield/pages/recovery/RecoveryPage.d.ts.map +1 -1
  78. package/dist/mybodyshield/pages/recovery/RecoveryPage.js +206 -200
  79. package/dist/mybodyshield/pages/recovery/RecoveryPage.js.map +1 -1
  80. package/dist/mybodyshield/pages/train/SessionDetailPage.d.ts.map +1 -1
  81. package/dist/mybodyshield/pages/train/SessionDetailPage.js +327 -294
  82. package/dist/mybodyshield/pages/train/SessionDetailPage.js.map +1 -1
  83. package/dist/mybodyshield/pages/train/SessionsPage.d.ts.map +1 -1
  84. package/dist/mybodyshield/pages/train/SessionsPage.js +180 -163
  85. package/dist/mybodyshield/pages/train/SessionsPage.js.map +1 -1
  86. package/dist/mybodyshield/utils/i18n.d.ts +1 -2
  87. package/dist/mybodyshield/utils/i18n.d.ts.map +1 -1
  88. package/dist/mybodyshield/utils/i18n.js +15 -0
  89. package/dist/mybodyshield/utils/i18n.js.map +1 -0
  90. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"GoalsPage.js","names":[],"sources":["../../../../src/mybodyshield/pages/goals/GoalsPage.tsx"],"sourcesContent":["/**\n * Goals — targets across training, nutrition and body composition.\n * @module mybodyshield/pages/goals\n *\n * Lists the member's `MybGoal`s with a status filter, per-goal progress meters\n * and inline \"update progress\" / \"mark achieved\" actions, plus a create flow.\n * KPIs (active count, achieved count, average active progress) are aggregated\n * client-side from the real `mybListGoals` endpoint. No mock data.\n */\n\nimport { useMemo, useState, type FC, type ReactNode } from 'react';\nimport { motion, useReducedMotion } from 'framer-motion';\nimport { Target, Plus, CheckCircle2, TrendingUp, Trophy, CalendarClock } 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 MiniBar,\n type FilterOption,\n} from '../../components';\nimport { Meter, IconChip, GOAL_TYPE_META, computeGoalProgress } from '../_shared/visuals';\nimport {\n useMybGoals,\n useMybCreateGoal,\n useMybUpdateGoal,\n useMybAchieveGoal,\n useOffsetPager,\n} from '../../hooks';\nimport type { MybGoal, MybGoalStatus, MybGoalType, MybCreateGoalInput } from '../../types';\nimport {\n formatDate,\n formatRelativeTime,\n formatNumber,\n formatPercent,\n formatEnumLabel,\n} from '../../utils/formatters';\n\nconst PAGE_SIZE = 12;\nconst KPI_LIMIT = 100;\n\ntype StatusFilter = MybGoalStatus | 'ALL';\n\n// Only statuses the backend can actually produce are offered as filters:\n// create → ACTIVE, achieve → ACHIEVED. There is no path to PAUSED/ABANDONED,\n// so those tabs would always be empty and mislead. StatusBadge still renders\n// every enum value for display safety.\nconst STATUS_FILTERS: FilterOption<StatusFilter>[] = [\n { value: 'ALL', label: 'All' },\n { value: 'ACTIVE', label: 'Active' },\n { value: 'ACHIEVED', label: 'Achieved' },\n];\n\nconst GOAL_TYPE_OPTIONS: MybGoalType[] = [\n 'WEIGHT_LOSS',\n 'MUSCLE_GAIN',\n 'STRENGTH',\n 'ENDURANCE',\n 'MOBILITY',\n 'GENERAL_FITNESS',\n];\n\n/** Parse a free-text numeric input → a finite number, or undefined when blank/invalid. */\nfunction numOrUndef(raw: string): number | undefined {\n const trimmed = raw.trim();\n if (trimmed === '') return undefined;\n const parsed = Number(trimmed);\n return Number.isFinite(parsed) ? parsed : undefined;\n}\n\n// ============================================================================\n// Create-goal dialog\n// ============================================================================\n\ninterface CreateGoalForm {\n title: string;\n type: MybGoalType;\n baselineValue: string;\n targetValue: string;\n unit: string;\n targetDate: string;\n description: string;\n}\n\nconst EMPTY_CREATE_FORM: CreateGoalForm = {\n title: '',\n type: 'GENERAL_FITNESS',\n baselineValue: '',\n targetValue: '',\n unit: '',\n targetDate: '',\n description: '',\n};\n\nfunction CreateGoalDialog({\n open,\n onOpenChange,\n}: {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n}) {\n const create = useMybCreateGoal();\n const [form, setForm] = useState<CreateGoalForm>(EMPTY_CREATE_FORM);\n const [error, setError] = useState<string | null>(null);\n\n const handleOpenChange = (next: boolean) => {\n if (!next) {\n setForm(EMPTY_CREATE_FORM);\n setError(null);\n }\n onOpenChange(next);\n };\n\n const submit = async () => {\n const title = form.title.trim();\n if (!title) {\n setError('Goal title is required.');\n return;\n }\n setError(null);\n const input: MybCreateGoalInput = {\n title,\n type: form.type,\n description: form.description.trim() || undefined,\n baselineValue: numOrUndef(form.baselineValue),\n targetValue: numOrUndef(form.targetValue),\n unit: form.unit.trim() || undefined,\n targetDate: form.targetDate ? new Date(form.targetDate).toISOString() : undefined,\n };\n try {\n await create.mutateAsync(input);\n setForm(EMPTY_CREATE_FORM);\n onOpenChange(false);\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to create goal.');\n }\n };\n\n return (\n <ResponsiveDialog open={open} onOpenChange={handleOpenChange}>\n <ResponsiveDialogContent>\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>New goal</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n Set a target across training, nutrition or body composition and track it to completion.\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n <form\n className=\"space-y-4\"\n onSubmit={(event) => {\n event.preventDefault();\n void submit();\n }}\n >\n {error && (\n <p className=\"rounded-lg bg-status-error-bg-subtle px-3 py-2 text-sm text-status-error-text\">\n {error}\n </p>\n )}\n <div className=\"space-y-2\">\n <Label htmlFor=\"myb-goal-title\">Title</Label>\n <Input\n id=\"myb-goal-title\"\n value={form.title}\n onChange={(event) => setForm((f) => ({ ...f, title: event.target.value }))}\n placeholder=\"e.g., Squat 140 kg\"\n />\n </div>\n <div className=\"space-y-2\">\n <Label htmlFor=\"myb-goal-type\">Type</Label>\n <Select\n value={form.type}\n // SelectItem values are exactly the MybGoalType members below, so the\n // string emitted by Radix is always a valid MybGoalType.\n onValueChange={(value) => setForm((f) => ({ ...f, type: value as MybGoalType }))}\n >\n <SelectTrigger id=\"myb-goal-type\">\n <SelectValue placeholder=\"Select a goal type\" />\n </SelectTrigger>\n <SelectContent>\n {GOAL_TYPE_OPTIONS.map((type) => (\n <SelectItem key={type} value={type}>\n {formatEnumLabel(type)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-3\">\n <div className=\"space-y-2\">\n <Label htmlFor=\"myb-goal-baseline\">Baseline</Label>\n <Input\n id=\"myb-goal-baseline\"\n type=\"number\"\n inputMode=\"decimal\"\n value={form.baselineValue}\n onChange={(event) => setForm((f) => ({ ...f, baselineValue: event.target.value }))}\n placeholder=\"0\"\n />\n </div>\n <div className=\"space-y-2\">\n <Label htmlFor=\"myb-goal-target\">Target</Label>\n <Input\n id=\"myb-goal-target\"\n type=\"number\"\n inputMode=\"decimal\"\n value={form.targetValue}\n onChange={(event) => setForm((f) => ({ ...f, targetValue: event.target.value }))}\n placeholder=\"100\"\n />\n </div>\n <div className=\"space-y-2\">\n <Label htmlFor=\"myb-goal-unit\">Unit</Label>\n <Input\n id=\"myb-goal-unit\"\n value={form.unit}\n onChange={(event) => setForm((f) => ({ ...f, unit: event.target.value }))}\n placeholder=\"kg / reps / min\"\n />\n </div>\n </div>\n <div className=\"space-y-2\">\n <Label htmlFor=\"myb-goal-date\">Target date</Label>\n <Input\n id=\"myb-goal-date\"\n type=\"date\"\n value={form.targetDate}\n onChange={(event) => setForm((f) => ({ ...f, targetDate: event.target.value }))}\n />\n </div>\n <div className=\"space-y-2\">\n <Label htmlFor=\"myb-goal-desc\">Description</Label>\n <Textarea\n id=\"myb-goal-desc\"\n rows={3}\n value={form.description}\n onChange={(event) => setForm((f) => ({ ...f, description: event.target.value }))}\n placeholder=\"Why this goal matters and how you'll get there.\"\n />\n </div>\n <ResponsiveDialogFooter>\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={() => handleOpenChange(false)}\n disabled={create.isPending}\n >\n Cancel\n </Button>\n <Button type=\"submit\" disabled={create.isPending}>\n {create.isPending ? 'Creating…' : 'Create goal'}\n </Button>\n </ResponsiveDialogFooter>\n </form>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n );\n}\n\n// ============================================================================\n// Update-progress dialog\n// ============================================================================\n\nfunction UpdateProgressDialog({\n goal,\n onOpenChange,\n}: {\n goal: MybGoal | null;\n onOpenChange: (open: boolean) => void;\n}) {\n const update = useMybUpdateGoal();\n const [value, setValue] = useState('');\n const [error, setError] = useState<string | null>(null);\n const [seededId, setSeededId] = useState<string | null>(null);\n\n // Seed the input with the goal's current value whenever the dialog target\n // changes — React's render-phase \"reset state when a prop changes\" pattern\n // (no effect, so no synchronous-setState-in-effect cascade).\n if (goal && goal.id !== seededId) {\n setSeededId(goal.id);\n setValue(String(goal.currentValue));\n setError(null);\n }\n\n const submit = async () => {\n if (!goal) return;\n const parsed = numOrUndef(value);\n if (parsed === undefined) {\n setError('Enter a valid number.');\n return;\n }\n setError(null);\n try {\n await update.mutateAsync({ id: goal.id, input: { currentValue: parsed } });\n onOpenChange(false);\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to update progress.');\n }\n };\n\n return (\n <ResponsiveDialog open={goal != null} onOpenChange={onOpenChange}>\n <ResponsiveDialogContent>\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>Update progress</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n {goal ? goal.title : 'Log your latest value for this goal.'}\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n <form\n className=\"space-y-4\"\n onSubmit={(event) => {\n event.preventDefault();\n void submit();\n }}\n >\n {error && (\n <p className=\"rounded-lg bg-status-error-bg-subtle px-3 py-2 text-sm text-status-error-text\">\n {error}\n </p>\n )}\n <div className=\"space-y-2\">\n <Label htmlFor=\"myb-goal-current\">\n Current value{goal?.unit ? ` (${goal.unit})` : ''}\n </Label>\n <Input\n id=\"myb-goal-current\"\n type=\"number\"\n inputMode=\"decimal\"\n autoFocus\n value={value}\n onChange={(event) => setValue(event.target.value)}\n placeholder=\"0\"\n />\n {goal && (\n <p className=\"text-xs text-text-tertiary\">\n Target: {formatNumber(goal.targetValue)}\n {goal.unit ? ` ${goal.unit}` : ''}\n </p>\n )}\n </div>\n <ResponsiveDialogFooter>\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={() => onOpenChange(false)}\n disabled={update.isPending}\n >\n Cancel\n </Button>\n <Button type=\"submit\" disabled={update.isPending}>\n {update.isPending ? 'Saving…' : 'Save progress'}\n </Button>\n </ResponsiveDialogFooter>\n </form>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n );\n}\n\n// ============================================================================\n// Motion — staggered card entrance + gentle hover lift (reduced-motion safe)\n// ============================================================================\n\nconst REVEAL_EASE: [number, number, number, number] = [0.22, 1, 0.36, 1];\n\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// ============================================================================\n// Goal card\n// ============================================================================\n\nfunction GoalCard({\n goal,\n index,\n onUpdateProgress,\n onAchieve,\n isAchieving,\n}: {\n goal: MybGoal;\n index: number;\n onUpdateProgress: (goal: MybGoal) => void;\n onAchieve: (goal: MybGoal) => void;\n isAchieving: boolean;\n}) {\n const meta = GOAL_TYPE_META[goal.type];\n const Icon = meta.icon;\n const pct = computeGoalProgress(goal);\n const unitSuffix = goal.unit ? ` ${goal.unit}` : '';\n const achieved = goal.status === 'ACHIEVED';\n\n return (\n <Reveal index={index} hover className=\"h-full\">\n <GlassCard\n treatment=\"glass\"\n glow={achieved}\n className=\"flex h-full flex-col gap-3.5 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={<Icon className=\"h-4 w-4\" />} tint={meta.tint} />\n <div className=\"min-w-0\">\n <p className=\"truncate text-sm font-semibold text-text-primary\">{goal.title}</p>\n <span className=\"mt-1 inline-flex items-center rounded-full bg-bg-sunken px-2 py-0.5 text-xs text-text-secondary\">\n {formatEnumLabel(goal.type)}\n </span>\n </div>\n </div>\n <StatusBadge status={goal.status} />\n </div>\n\n {goal.description && (\n <p className=\"line-clamp-2 text-xs text-text-secondary\">{goal.description}</p>\n )}\n\n <div className=\"space-y-2\">\n <div className=\"flex items-end justify-between gap-2\">\n <span className=\"tabular-nums text-sm text-text-secondary\">\n <span className=\"font-semibold text-text-primary\">\n {formatNumber(goal.currentValue)}\n {unitSuffix}\n </span>\n <span className=\"text-text-tertiary\">\n {' '}\n / {formatNumber(goal.targetValue)}\n {unitSuffix}\n </span>\n </span>\n <span className=\"text-lg font-bold leading-none tabular-nums text-action-primary-bg\">\n {formatPercent(pct)}\n </span>\n </div>\n <Meter value={pct} height={10} />\n </div>\n\n {(goal.targetDate || (achieved && goal.achievedAt)) && (\n <div className=\"flex flex-wrap items-center gap-x-4 gap-y-1 text-xs text-text-tertiary\">\n {goal.targetDate && (\n <span className=\"inline-flex items-center gap-1\">\n <CalendarClock className=\"h-3.5 w-3.5\" />\n Target {formatDate(goal.targetDate)}\n <span className=\"text-text-secondary\">· {formatRelativeTime(goal.targetDate)}</span>\n </span>\n )}\n {achieved && goal.achievedAt && (\n <span className=\"inline-flex items-center gap-1 text-status-success-text\">\n <CheckCircle2 className=\"h-3.5 w-3.5\" />\n Achieved {formatDate(goal.achievedAt)}\n </span>\n )}\n </div>\n )}\n\n <div className=\"mt-auto flex flex-wrap gap-2 pt-1\">\n <Button variant=\"outline\" size=\"sm\" onClick={() => onUpdateProgress(goal)}>\n Update progress\n </Button>\n {goal.status === 'ACTIVE' && (\n <Button size=\"sm\" onClick={() => onAchieve(goal)} disabled={isAchieving}>\n {isAchieving ? 'Marking…' : 'Mark achieved'}\n </Button>\n )}\n </div>\n </GlassCard>\n </Reveal>\n );\n}\n\n// ============================================================================\n// Page\n// ============================================================================\n\nexport function GoalsPage() {\n const [statusFilter, setStatusFilter] = useState<StatusFilter>('ALL');\n const [createOpen, setCreateOpen] = useState(false);\n const [progressGoal, setProgressGoal] = useState<MybGoal | null>(null);\n const [achievingId, setAchievingId] = useState<string | null>(null);\n const [actionError, setActionError] = useState<string | null>(null);\n\n const pager = useOffsetPager(PAGE_SIZE);\n\n const goalsQuery = useMybGoals({\n status: statusFilter === 'ALL' ? undefined : statusFilter,\n pagination: { limit: PAGE_SIZE, offset: pager.offset },\n });\n\n // Separate bounded queries power the KPI row so the counts stay accurate\n // regardless of the active filter / page (connections expose no total count).\n const activeGoals = useMybGoals({ status: 'ACTIVE', pagination: { limit: KPI_LIMIT } });\n const achievedGoals = useMybGoals({ status: 'ACHIEVED', pagination: { limit: KPI_LIMIT } });\n\n const achieve = useMybAchieveGoal();\n\n const rows = goalsQuery.data?.items ?? [];\n const activeItems = activeGoals.data?.items ?? [];\n const achievedItems = achievedGoals.data?.items ?? [];\n\n const avgActiveProgress = useMemo(() => {\n if (activeItems.length === 0) return 0;\n const sum = activeItems.reduce((acc, goal) => acc + computeGoalProgress(goal), 0);\n return sum / activeItems.length;\n }, [activeItems]);\n\n const handleFilterChange = (next: StatusFilter) => {\n setStatusFilter(next);\n pager.reset();\n };\n\n const handleAchieve = async (goal: MybGoal) => {\n setAchievingId(goal.id);\n setActionError(null);\n try {\n await achieve.mutateAsync(goal.id);\n } catch (err) {\n setActionError(err instanceof Error ? err.message : 'Failed to mark goal as achieved.');\n } finally {\n setAchievingId(null);\n }\n };\n\n const showLoading = goalsQuery.isLoading && !goalsQuery.data;\n\n return (\n <PageLayout\n title=\"Goals\"\n description=\"Targets across training, nutrition and body composition.\"\n actions={\n <Button onClick={() => setCreateOpen(true)}>\n <Plus className=\"mr-2 h-4 w-4\" />\n New goal\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={<Target className=\"h-5 w-5\" />}\n value={formatNumber(activeGoals.data ? activeItems.length : undefined)}\n label=\"Active goals\"\n />\n </Reveal>\n <Reveal index={1} className=\"h-full\">\n <MetricsCard\n className=\"h-full\"\n icon={<Trophy className=\"h-5 w-5\" />}\n value={formatNumber(achievedGoals.data ? achievedItems.length : undefined)}\n label=\"Achieved\"\n accent=\"success\"\n />\n </Reveal>\n <Reveal index={2} className=\"h-full\">\n <MetricsCard\n className=\"h-full\"\n icon={<TrendingUp className=\"h-5 w-5\" />}\n value={activeGoals.data ? formatPercent(avgActiveProgress) : '—'}\n label=\"Avg progress (active)\"\n footer={\n <MiniBar value={avgActiveProgress} height={4} barClassName=\"bg-action-primary-bg\" />\n }\n />\n </Reveal>\n </div>\n\n {/* Filter */}\n <div className=\"mt-6\">\n <FilterTabs options={STATUS_FILTERS} value={statusFilter} onChange={handleFilterChange} />\n </div>\n\n {actionError && (\n <p className=\"mt-4 rounded-lg bg-status-error-bg-subtle px-3 py-2 text-sm text-status-error-text\">\n {actionError}\n </p>\n )}\n\n {/* Grid */}\n <div className=\"mt-4\">\n {showLoading ? (\n <LoadingView label=\"Loading your goals…\" />\n ) : goalsQuery.isError ? (\n <ErrorView\n message=\"We couldn't load your goals.\"\n onRetry={() => void goalsQuery.refetch()}\n />\n ) : rows.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n icon={<Target className=\"h-10 w-10\" />}\n title={statusFilter === 'ALL' ? 'No goals yet' : 'No goals match this filter'}\n description={\n statusFilter === 'ALL'\n ? 'Set a goal to give your training and nutrition a clear target to work toward.'\n : 'Try a different status filter, or create a new goal.'\n }\n action={\n <Button onClick={() => setCreateOpen(true)}>\n <Plus className=\"mr-2 h-4 w-4\" />\n New goal\n </Button>\n }\n />\n ) : (\n <>\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n {rows.map((goal, i) => (\n <GoalCard\n key={goal.id}\n goal={goal}\n index={i}\n onUpdateProgress={setProgressGoal}\n onAchieve={(g) => void handleAchieve(g)}\n isAchieving={achievingId === goal.id}\n />\n ))}\n </div>\n <Pagination\n page={pager.page}\n pageSize={PAGE_SIZE}\n itemCount={rows.length}\n hasMore={goalsQuery.data?.pageInfo?.hasMore ?? false}\n onPageChange={(target) => pager.onPageChange(target)}\n />\n </>\n )}\n </div>\n\n <CreateGoalDialog open={createOpen} onOpenChange={setCreateOpen} />\n <UpdateProgressDialog\n goal={progressGoal}\n onOpenChange={(open) => {\n if (!open) setProgressGoal(null);\n }}\n />\n </PageLayout>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"GoalsPage.js","names":[],"sources":["../../../../src/mybodyshield/pages/goals/GoalsPage.tsx"],"sourcesContent":["/**\n * Goals — targets across training, nutrition and body composition.\n * @module mybodyshield/pages/goals\n *\n * Lists the member's `MybGoal`s with a status filter, per-goal progress meters\n * and inline \"update progress\" / \"mark achieved\" actions, plus a create flow.\n * KPIs (active count, achieved count, average active progress) are aggregated\n * client-side from the real `mybListGoals` endpoint. No mock data.\n */\n\nimport { useMemo, useState, type FC, type ReactNode } from 'react';\nimport { motion, useReducedMotion } from 'framer-motion';\nimport { Target, Plus, CheckCircle2, TrendingUp, Trophy, CalendarClock } 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 MiniBar,\n type FilterOption,\n} from '../../components';\nimport { Meter, IconChip, GOAL_TYPE_META, computeGoalProgress } from '../_shared/visuals';\nimport {\n useMybGoals,\n useMybCreateGoal,\n useMybUpdateGoal,\n useMybAchieveGoal,\n useOffsetPager,\n useTr,\n} from '../../hooks';\nimport type { MybGoal, MybGoalStatus, MybGoalType, MybCreateGoalInput } from '../../types';\nimport {\n formatDate,\n formatRelativeTime,\n formatNumber,\n formatPercent,\n formatEnumLabel,\n} from '../../utils/formatters';\n\nconst PAGE_SIZE = 12;\nconst KPI_LIMIT = 100;\n\ntype StatusFilter = MybGoalStatus | 'ALL';\n\n// Only statuses the backend can actually produce are offered as filters:\n// create → ACTIVE, achieve → ACHIEVED. There is no path to PAUSED/ABANDONED,\n// so those tabs would always be empty and mislead. StatusBadge still renders\n// every enum value for display safety.\nfunction useStatusFilters(t: ReturnType<typeof useTr>): FilterOption<StatusFilter>[] {\n return [\n { value: 'ALL', label: t('mybodyshield.common.all', 'All') },\n { value: 'ACTIVE', label: t('mybodyshield.common.active', 'Active') },\n { value: 'ACHIEVED', label: t('mybodyshield.pages.goals.achieved', 'Achieved') },\n ];\n}\n\nconst GOAL_TYPE_OPTIONS: MybGoalType[] = [\n 'WEIGHT_LOSS',\n 'MUSCLE_GAIN',\n 'STRENGTH',\n 'ENDURANCE',\n 'MOBILITY',\n 'GENERAL_FITNESS',\n];\n\n/** Parse a free-text numeric input → a finite number, or undefined when blank/invalid. */\nfunction numOrUndef(raw: string): number | undefined {\n const trimmed = raw.trim();\n if (trimmed === '') return undefined;\n const parsed = Number(trimmed);\n return Number.isFinite(parsed) ? parsed : undefined;\n}\n\n// ============================================================================\n// Create-goal dialog\n// ============================================================================\n\ninterface CreateGoalForm {\n title: string;\n type: MybGoalType;\n baselineValue: string;\n targetValue: string;\n unit: string;\n targetDate: string;\n description: string;\n}\n\nconst EMPTY_CREATE_FORM: CreateGoalForm = {\n title: '',\n type: 'GENERAL_FITNESS',\n baselineValue: '',\n targetValue: '',\n unit: '',\n targetDate: '',\n description: '',\n};\n\nfunction CreateGoalDialog({\n open,\n onOpenChange,\n}: {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n}) {\n const t = useTr();\n const create = useMybCreateGoal();\n const [form, setForm] = useState<CreateGoalForm>(EMPTY_CREATE_FORM);\n const [error, setError] = useState<string | null>(null);\n\n const handleOpenChange = (next: boolean) => {\n if (!next) {\n setForm(EMPTY_CREATE_FORM);\n setError(null);\n }\n onOpenChange(next);\n };\n\n const submit = async () => {\n const title = form.title.trim();\n if (!title) {\n setError(t('mybodyshield.pages.goals.goalTitleRequired', 'Goal title is required.'));\n return;\n }\n setError(null);\n const input: MybCreateGoalInput = {\n title,\n type: form.type,\n description: form.description.trim() || undefined,\n baselineValue: numOrUndef(form.baselineValue),\n targetValue: numOrUndef(form.targetValue),\n unit: form.unit.trim() || undefined,\n targetDate: form.targetDate ? new Date(form.targetDate).toISOString() : undefined,\n };\n try {\n await create.mutateAsync(input);\n setForm(EMPTY_CREATE_FORM);\n onOpenChange(false);\n } catch (err) {\n setError(\n err instanceof Error\n ? err.message\n : t('mybodyshield.pages.goals.failedToCreate', 'Failed to create goal.')\n );\n }\n };\n\n return (\n <ResponsiveDialog open={open} onOpenChange={handleOpenChange}>\n <ResponsiveDialogContent>\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>\n {t('mybodyshield.pages.goals.dialogTitle', 'New goal')}\n </ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n {t(\n 'mybodyshield.pages.goals.dialogDescription',\n 'Set a target across training, nutrition or body composition and track it to completion.'\n )}\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n <form\n className=\"space-y-4\"\n onSubmit={(event) => {\n event.preventDefault();\n void submit();\n }}\n >\n {error && (\n <p className=\"rounded-lg bg-status-error-bg-subtle px-3 py-2 text-sm text-status-error-text\">\n {error}\n </p>\n )}\n <div className=\"space-y-2\">\n <Label htmlFor=\"myb-goal-title\">\n {t('mybodyshield.pages.goals.titleLabel', 'Title')}\n </Label>\n <Input\n id=\"myb-goal-title\"\n value={form.title}\n onChange={(event) => setForm((f) => ({ ...f, title: event.target.value }))}\n placeholder={t('mybodyshield.pages.goals.titlePlaceholder', 'e.g., Squat 140 kg')}\n />\n </div>\n <div className=\"space-y-2\">\n <Label htmlFor=\"myb-goal-type\">{t('mybodyshield.pages.goals.goalType', 'Type')}</Label>\n <Select\n value={form.type}\n // SelectItem values are exactly the MybGoalType members below, so the\n // string emitted by Radix is always a valid MybGoalType.\n onValueChange={(value) => setForm((f) => ({ ...f, type: value as MybGoalType }))}\n >\n <SelectTrigger id=\"myb-goal-type\">\n <SelectValue\n placeholder={t(\n 'mybodyshield.pages.goals.goalTypePlaceholder',\n 'Select a goal type'\n )}\n />\n </SelectTrigger>\n <SelectContent>\n {GOAL_TYPE_OPTIONS.map((type) => (\n <SelectItem key={type} value={type}>\n {formatEnumLabel(type)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-3\">\n <div className=\"space-y-2\">\n <Label htmlFor=\"myb-goal-baseline\">\n {t('mybodyshield.pages.goals.baseline', 'Baseline')}\n </Label>\n <Input\n id=\"myb-goal-baseline\"\n type=\"number\"\n inputMode=\"decimal\"\n value={form.baselineValue}\n onChange={(event) => setForm((f) => ({ ...f, baselineValue: event.target.value }))}\n placeholder=\"0\"\n />\n </div>\n <div className=\"space-y-2\">\n <Label htmlFor=\"myb-goal-target\">\n {t('mybodyshield.pages.goals.target', 'Target')}\n </Label>\n <Input\n id=\"myb-goal-target\"\n type=\"number\"\n inputMode=\"decimal\"\n value={form.targetValue}\n onChange={(event) => setForm((f) => ({ ...f, targetValue: event.target.value }))}\n placeholder=\"100\"\n />\n </div>\n <div className=\"space-y-2\">\n <Label htmlFor=\"myb-goal-unit\">{t('mybodyshield.common.unit', 'Unit')}</Label>\n <Input\n id=\"myb-goal-unit\"\n value={form.unit}\n onChange={(event) => setForm((f) => ({ ...f, unit: event.target.value }))}\n placeholder={t('mybodyshield.pages.goals.unitPlaceholder', 'kg / reps / min')}\n />\n </div>\n </div>\n <div className=\"space-y-2\">\n <Label htmlFor=\"myb-goal-date\">\n {t('mybodyshield.pages.goals.targetDate', 'Target date')}\n </Label>\n <Input\n id=\"myb-goal-date\"\n type=\"date\"\n value={form.targetDate}\n onChange={(event) => setForm((f) => ({ ...f, targetDate: event.target.value }))}\n />\n </div>\n <div className=\"space-y-2\">\n <Label htmlFor=\"myb-goal-desc\">\n {t('mybodyshield.common.description', 'Description')}\n </Label>\n <Textarea\n id=\"myb-goal-desc\"\n rows={3}\n value={form.description}\n onChange={(event) => setForm((f) => ({ ...f, description: event.target.value }))}\n placeholder={t(\n 'mybodyshield.pages.goals.descriptionPlaceholder',\n \"Why this goal matters and how you'll get there.\"\n )}\n />\n </div>\n <ResponsiveDialogFooter>\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={() => handleOpenChange(false)}\n disabled={create.isPending}\n >\n {t('mybodyshield.common.cancel', 'Cancel')}\n </Button>\n <Button type=\"submit\" disabled={create.isPending}>\n {create.isPending\n ? t('mybodyshield.common.creating', 'Creating…')\n : t('mybodyshield.pages.goals.createGoalButton', 'Create goal')}\n </Button>\n </ResponsiveDialogFooter>\n </form>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n );\n}\n\n// ============================================================================\n// Update-progress dialog\n// ============================================================================\n\nfunction UpdateProgressDialog({\n goal,\n onOpenChange,\n}: {\n goal: MybGoal | null;\n onOpenChange: (open: boolean) => void;\n}) {\n const t = useTr();\n const update = useMybUpdateGoal();\n const [value, setValue] = useState('');\n const [error, setError] = useState<string | null>(null);\n const [seededId, setSeededId] = useState<string | null>(null);\n\n // Seed the input with the goal's current value whenever the dialog target\n // changes — React's render-phase \"reset state when a prop changes\" pattern\n // (no effect, so no synchronous-setState-in-effect cascade).\n if (goal && goal.id !== seededId) {\n setSeededId(goal.id);\n setValue(String(goal.currentValue));\n setError(null);\n }\n\n const submit = async () => {\n if (!goal) return;\n const parsed = numOrUndef(value);\n if (parsed === undefined) {\n setError(t('mybodyshield.pages.goals.enterValidNumber', 'Enter a valid number.'));\n return;\n }\n setError(null);\n try {\n await update.mutateAsync({ id: goal.id, input: { currentValue: parsed } });\n onOpenChange(false);\n } catch (err) {\n setError(\n err instanceof Error\n ? err.message\n : t('mybodyshield.pages.goals.failedToUpdate', 'Failed to update progress.')\n );\n }\n };\n\n return (\n <ResponsiveDialog open={goal != null} onOpenChange={onOpenChange}>\n <ResponsiveDialogContent>\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>\n {t('mybodyshield.pages.goals.updateProgressTitle', 'Update progress')}\n </ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n {goal\n ? goal.title\n : t(\n 'mybodyshield.pages.goals.updateProgressDescription',\n 'Log your latest value for this goal.'\n )}\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n <form\n className=\"space-y-4\"\n onSubmit={(event) => {\n event.preventDefault();\n void submit();\n }}\n >\n {error && (\n <p className=\"rounded-lg bg-status-error-bg-subtle px-3 py-2 text-sm text-status-error-text\">\n {error}\n </p>\n )}\n <div className=\"space-y-2\">\n <Label htmlFor=\"myb-goal-current\">\n {t('mybodyshield.pages.goals.currentValue', 'Current value{{unit}}', {\n unit: goal?.unit ? ` (${goal.unit})` : '',\n })}\n </Label>\n <Input\n id=\"myb-goal-current\"\n type=\"number\"\n inputMode=\"decimal\"\n autoFocus\n value={value}\n onChange={(event) => setValue(event.target.value)}\n placeholder=\"0\"\n />\n {goal && (\n <p className=\"text-xs text-text-tertiary\">\n {t('mybodyshield.pages.goals.targetValue', 'Target: {{value}}', {\n value: formatNumber(goal.targetValue) + (goal.unit ? ` ${goal.unit}` : ''),\n })}\n </p>\n )}\n </div>\n <ResponsiveDialogFooter>\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={() => onOpenChange(false)}\n disabled={update.isPending}\n >\n {t('mybodyshield.common.cancel', 'Cancel')}\n </Button>\n <Button type=\"submit\" disabled={update.isPending}>\n {update.isPending\n ? t('mybodyshield.common.saving', 'Saving…')\n : t('mybodyshield.pages.goals.saveProgress', 'Save progress')}\n </Button>\n </ResponsiveDialogFooter>\n </form>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n );\n}\n\n// ============================================================================\n// Motion — staggered card entrance + gentle hover lift (reduced-motion safe)\n// ============================================================================\n\nconst REVEAL_EASE: [number, number, number, number] = [0.22, 1, 0.36, 1];\n\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// ============================================================================\n// Goal card\n// ============================================================================\n\nfunction GoalCard({\n goal,\n index,\n onUpdateProgress,\n onAchieve,\n isAchieving,\n}: {\n goal: MybGoal;\n index: number;\n onUpdateProgress: (goal: MybGoal) => void;\n onAchieve: (goal: MybGoal) => void;\n isAchieving: boolean;\n}) {\n const t = useTr();\n const meta = GOAL_TYPE_META[goal.type];\n const Icon = meta.icon;\n const pct = computeGoalProgress(goal);\n const unitSuffix = goal.unit ? ` ${goal.unit}` : '';\n const achieved = goal.status === 'ACHIEVED';\n\n return (\n <Reveal index={index} hover className=\"h-full\">\n <GlassCard\n treatment=\"glass\"\n glow={achieved}\n className=\"flex h-full flex-col gap-3.5 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={<Icon className=\"h-4 w-4\" />} tint={meta.tint} />\n <div className=\"min-w-0\">\n <p className=\"truncate text-sm font-semibold text-text-primary\">{goal.title}</p>\n <span className=\"mt-1 inline-flex items-center rounded-full bg-bg-sunken px-2 py-0.5 text-xs text-text-secondary\">\n {formatEnumLabel(goal.type)}\n </span>\n </div>\n </div>\n <StatusBadge status={goal.status} />\n </div>\n\n {goal.description && (\n <p className=\"line-clamp-2 text-xs text-text-secondary\">{goal.description}</p>\n )}\n\n <div className=\"space-y-2\">\n <div className=\"flex items-end justify-between gap-2\">\n <span className=\"tabular-nums text-sm text-text-secondary\">\n <span className=\"font-semibold text-text-primary\">\n {formatNumber(goal.currentValue)}\n {unitSuffix}\n </span>\n <span className=\"text-text-tertiary\">\n {' '}\n / {formatNumber(goal.targetValue)}\n {unitSuffix}\n </span>\n </span>\n <span className=\"text-lg font-bold leading-none tabular-nums text-action-primary-bg\">\n {formatPercent(pct)}\n </span>\n </div>\n <Meter value={pct} height={10} />\n </div>\n\n {(goal.targetDate || (achieved && goal.achievedAt)) && (\n <div className=\"flex flex-wrap items-center gap-x-4 gap-y-1 text-xs text-text-tertiary\">\n {goal.targetDate && (\n <span className=\"inline-flex items-center gap-1\">\n <CalendarClock className=\"h-3.5 w-3.5\" />\n {t('mybodyshield.pages.goals.targetDateLabel', 'Target {{date}}', {\n date: formatDate(goal.targetDate),\n })}\n <span className=\"text-text-secondary\">· {formatRelativeTime(goal.targetDate)}</span>\n </span>\n )}\n {achieved && goal.achievedAt && (\n <span className=\"inline-flex items-center gap-1 text-status-success-text\">\n <CheckCircle2 className=\"h-3.5 w-3.5\" />\n {t('mybodyshield.pages.goals.achievedDateLabel', 'Achieved {{date}}', {\n date: formatDate(goal.achievedAt),\n })}\n </span>\n )}\n </div>\n )}\n\n <div className=\"mt-auto flex flex-wrap gap-2 pt-1\">\n <Button variant=\"outline\" size=\"sm\" onClick={() => onUpdateProgress(goal)}>\n {t('mybodyshield.pages.goals.updateProgress', 'Update progress')}\n </Button>\n {goal.status === 'ACTIVE' && (\n <Button size=\"sm\" onClick={() => onAchieve(goal)} disabled={isAchieving}>\n {isAchieving\n ? t('mybodyshield.pages.goals.marking', 'Marking…')\n : t('mybodyshield.pages.goals.markAchieved', 'Mark achieved')}\n </Button>\n )}\n </div>\n </GlassCard>\n </Reveal>\n );\n}\n\n// ============================================================================\n// Page\n// ============================================================================\n\nexport function GoalsPage() {\n const t = useTr();\n const [statusFilter, setStatusFilter] = useState<StatusFilter>('ALL');\n const [createOpen, setCreateOpen] = useState(false);\n const [progressGoal, setProgressGoal] = useState<MybGoal | null>(null);\n const [achievingId, setAchievingId] = useState<string | null>(null);\n const [actionError, setActionError] = useState<string | null>(null);\n\n const pager = useOffsetPager(PAGE_SIZE);\n\n const goalsQuery = useMybGoals({\n status: statusFilter === 'ALL' ? undefined : statusFilter,\n pagination: { limit: PAGE_SIZE, offset: pager.offset },\n });\n\n // Separate bounded queries power the KPI row so the counts stay accurate\n // regardless of the active filter / page (connections expose no total count).\n const activeGoals = useMybGoals({ status: 'ACTIVE', pagination: { limit: KPI_LIMIT } });\n const achievedGoals = useMybGoals({ status: 'ACHIEVED', pagination: { limit: KPI_LIMIT } });\n\n const achieve = useMybAchieveGoal();\n\n const rows = goalsQuery.data?.items ?? [];\n const activeItems = activeGoals.data?.items ?? [];\n const achievedItems = achievedGoals.data?.items ?? [];\n\n const avgActiveProgress = useMemo(() => {\n if (activeItems.length === 0) return 0;\n const sum = activeItems.reduce((acc, goal) => acc + computeGoalProgress(goal), 0);\n return sum / activeItems.length;\n }, [activeItems]);\n\n const handleFilterChange = (next: StatusFilter) => {\n setStatusFilter(next);\n pager.reset();\n };\n\n const handleAchieve = async (goal: MybGoal) => {\n setAchievingId(goal.id);\n setActionError(null);\n try {\n await achieve.mutateAsync(goal.id);\n } catch (err) {\n setActionError(\n err instanceof Error\n ? err.message\n : t('mybodyshield.pages.goals.failedToAchieve', 'Failed to mark goal as achieved.')\n );\n } finally {\n setAchievingId(null);\n }\n };\n\n const showLoading = goalsQuery.isLoading && !goalsQuery.data;\n const STATUS_FILTERS = useStatusFilters(t);\n\n return (\n <PageLayout\n title={t('mybodyshield.pages.goals.title', 'Goals')}\n description={t(\n 'mybodyshield.pages.goals.description',\n 'Targets across training, nutrition and body composition.'\n )}\n actions={\n <Button onClick={() => setCreateOpen(true)}>\n <Plus className=\"mr-2 h-4 w-4\" />\n {t('mybodyshield.pages.goals.newGoal', 'New goal')}\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={<Target className=\"h-5 w-5\" />}\n value={formatNumber(activeGoals.data ? activeItems.length : undefined)}\n label={t('mybodyshield.pages.goals.activeGoals', 'Active goals')}\n />\n </Reveal>\n <Reveal index={1} className=\"h-full\">\n <MetricsCard\n className=\"h-full\"\n icon={<Trophy className=\"h-5 w-5\" />}\n value={formatNumber(achievedGoals.data ? achievedItems.length : undefined)}\n label={t('mybodyshield.pages.goals.achieved', 'Achieved')}\n accent=\"success\"\n />\n </Reveal>\n <Reveal index={2} className=\"h-full\">\n <MetricsCard\n className=\"h-full\"\n icon={<TrendingUp className=\"h-5 w-5\" />}\n value={activeGoals.data ? formatPercent(avgActiveProgress) : '—'}\n label={t('mybodyshield.pages.goals.avgProgress', 'Avg progress (active)')}\n footer={\n <MiniBar value={avgActiveProgress} height={4} barClassName=\"bg-action-primary-bg\" />\n }\n />\n </Reveal>\n </div>\n\n {/* Filter */}\n <div className=\"mt-6\">\n <FilterTabs options={STATUS_FILTERS} value={statusFilter} onChange={handleFilterChange} />\n </div>\n\n {actionError && (\n <p className=\"mt-4 rounded-lg bg-status-error-bg-subtle px-3 py-2 text-sm text-status-error-text\">\n {actionError}\n </p>\n )}\n\n {/* Grid */}\n <div className=\"mt-4\">\n {showLoading ? (\n <LoadingView label={t('mybodyshield.pages.goals.loading', 'Loading your goals…')} />\n ) : goalsQuery.isError ? (\n <ErrorView\n message={t('mybodyshield.pages.goals.error', \"We couldn't load your goals.\")}\n onRetry={() => void goalsQuery.refetch()}\n />\n ) : rows.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n icon={<Target className=\"h-10 w-10\" />}\n title={\n statusFilter === 'ALL'\n ? t('mybodyshield.pages.goals.emptyTitle', 'No goals yet')\n : t('mybodyshield.pages.goals.emptyFilteredTitle', 'No goals match this filter')\n }\n description={\n statusFilter === 'ALL'\n ? t(\n 'mybodyshield.pages.goals.emptyDescription',\n 'Set a goal to give your training and nutrition a clear target to work toward.'\n )\n : t(\n 'mybodyshield.pages.goals.emptyFilteredDescription',\n 'Try a different status filter, or create a new goal.'\n )\n }\n action={\n <Button onClick={() => setCreateOpen(true)}>\n <Plus className=\"mr-2 h-4 w-4\" />\n {t('mybodyshield.pages.goals.newGoal', 'New goal')}\n </Button>\n }\n />\n ) : (\n <>\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n {rows.map((goal, i) => (\n <GoalCard\n key={goal.id}\n goal={goal}\n index={i}\n onUpdateProgress={setProgressGoal}\n onAchieve={(g) => void handleAchieve(g)}\n isAchieving={achievingId === goal.id}\n />\n ))}\n </div>\n <Pagination\n page={pager.page}\n pageSize={PAGE_SIZE}\n itemCount={rows.length}\n hasMore={goalsQuery.data?.pageInfo?.hasMore ?? false}\n onPageChange={(target) => pager.onPageChange(target)}\n />\n </>\n )}\n </div>\n\n <CreateGoalDialog open={createOpen} onOpenChange={setCreateOpen} />\n <UpdateProgressDialog\n goal={progressGoal}\n onOpenChange={(open) => {\n if (!open) setProgressGoal(null);\n }}\n />\n </PageLayout>\n );\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"InsightsCharts.d.ts","sourceRoot":"","sources":["../../../../src/mybodyshield/pages/insights/InsightsCharts.tsx"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AA2BH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AA4GlD,wBAAgB,WAAW,CAAC,EAAE,KAAK,EAAE,EAAE;IAAE,KAAK,EAAE,cAAc,EAAE,CAAA;CAAE,+BAgFjE;AAMD,wBAAgB,mBAAmB,CAAC,EAAE,KAAK,EAAE,EAAE;IAAE,KAAK,EAAE,cAAc,EAAE,CAAA;CAAE,+BAyFzE;AAMD,wBAAgB,UAAU,CAAC,EAAE,KAAK,EAAE,EAAE;IAAE,KAAK,EAAE,cAAc,EAAE,CAAA;CAAE,+BAoDhE;AAMD,wBAAgB,YAAY,CAAC,EAAE,KAAK,EAAE,EAAE;IAAE,KAAK,EAAE,cAAc,EAAE,CAAA;CAAE,+BA2DlE;AA6ED,wBAAgB,eAAe,CAAC,EAAE,KAAK,EAAE,EAAE;IAAE,KAAK,EAAE,cAAc,EAAE,CAAA;CAAE,+BAkDrE"}
1
+ {"version":3,"file":"InsightsCharts.d.ts","sourceRoot":"","sources":["../../../../src/mybodyshield/pages/insights/InsightsCharts.tsx"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AA4BH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AA4GlD,wBAAgB,WAAW,CAAC,EAAE,KAAK,EAAE,EAAE;IAAE,KAAK,EAAE,cAAc,EAAE,CAAA;CAAE,+BAsFjE;AAMD,wBAAgB,mBAAmB,CAAC,EAAE,KAAK,EAAE,EAAE;IAAE,KAAK,EAAE,cAAc,EAAE,CAAA;CAAE,+BA+FzE;AAMD,wBAAgB,UAAU,CAAC,EAAE,KAAK,EAAE,EAAE;IAAE,KAAK,EAAE,cAAc,EAAE,CAAA;CAAE,+BAyEhE;AAMD,wBAAgB,YAAY,CAAC,EAAE,KAAK,EAAE,EAAE;IAAE,KAAK,EAAE,cAAc,EAAE,CAAA;CAAE,+BAiElE;AAkHD,wBAAgB,eAAe,CAAC,EAAE,KAAK,EAAE,EAAE;IAAE,KAAK,EAAE,cAAc,EAAE,CAAA;CAAE,+BAqErE"}