@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":"AiControlsPage.js","names":[],"sources":["../../../../src/mybodyshield/pages/ai/AiControlsPage.tsx"],"sourcesContent":["/**\n * AI controls — provider, model, daily allowance and operation history.\n * @module mybodyshield/pages/ai\n *\n * BOFF-5949: per-member AI settings surface. Lets the member choose a provider\n * (Gemini / OpenAI / Anthropic), the model name, a daily operation allowance,\n * and an optional provider API key. Shows the redacted audit log of AI ops.\n */\n\nimport { useMemo, useState } from 'react';\nimport { Sparkles, KeyRound, Bot, Activity, Save, ShieldCheck, ShieldOff } from 'lucide-react';\nimport {\n Button,\n Input,\n Label,\n Select,\n SelectTrigger,\n SelectContent,\n SelectItem,\n SelectValue,\n Switch,\n GlassCard,\n Spinner,\n} from '@burdenoff/fe-libs/ui';\nimport {\n PageLayout,\n MetricsCard,\n LoadingView,\n ErrorView,\n Section,\n DataTable,\n type Column,\n} from '../../components';\nimport {\n useMybAiSettings,\n useMybUpdateAiSettings,\n useMybAiOperationLogs,\n useMybAiProcessingConsent,\n useMybSetAiProcessingConsent,\n} from '../../hooks';\nimport type {\n MybAiProvider,\n MybAiSettings,\n MybAiOperationLog,\n MybAiOperationLogConnection,\n MybUpdateAiSettingsInput,\n} from '../../types';\nimport { formatDateTime, formatRelativeTime, formatEnumLabel } from '../../utils/formatters';\nimport { IconChip } from '../_shared/visuals';\nimport type { UseQueryResult } from '@tanstack/react-query';\n\nconst AI_PROVIDERS: MybAiProvider[] = ['GEMINI', 'OPENAI', 'ANTHROPIC'];\n\nconst PROVIDER_LABELS: Record<MybAiProvider, string> = {\n GEMINI: 'Google Gemini',\n OPENAI: 'OpenAI',\n ANTHROPIC: 'Anthropic',\n};\n\nconst DEFAULT_MODELS: Record<MybAiProvider, string> = {\n GEMINI: 'gemini-2.5-flash',\n OPENAI: 'gpt-4o-mini',\n ANTHROPIC: 'claude-3-5-haiku-latest',\n};\n\ninterface AiSettingsForm {\n provider: MybAiProvider;\n modelName: string;\n dailyAllowance: string;\n providerKey: string;\n clearKey: boolean;\n}\n\nconst makeFormFromSettings = (settings: MybAiSettings): AiSettingsForm => ({\n provider: settings.provider,\n modelName: settings.modelName,\n dailyAllowance: String(settings.dailyAllowance),\n providerKey: '',\n clearKey: false,\n});\n\n/**\n * AI-processing consent gate (BOFF-3838).\n *\n * The settings on this page only take effect while consent is granted: the\n * service checks `mybMyAiProcessingConsent` before it will generate a guidance\n * summary, and returns a null summary without it. Surfacing the state here (and\n * not only under Settings) stops a member from configuring a provider and model\n * that can never run. Reads and writes the same query key as the Settings page,\n * so the two stay in sync.\n */\nfunction AiConsentGate() {\n const consent = useMybAiProcessingConsent();\n const setConsent = useMybSetAiProcessingConsent();\n\n if (consent.isLoading) {\n return <LoadingView label=\"Loading AI-processing consent…\" />;\n }\n\n if (consent.isError) {\n return (\n <ErrorView\n message=\"We couldn't load your AI-processing consent.\"\n onRetry={() => consent.refetch()}\n />\n );\n }\n\n const enabled = consent.data?.enabled ?? false;\n\n return (\n <GlassCard treatment=\"plain\" className=\"p-5\">\n <div className=\"flex flex-wrap items-start justify-between gap-4\">\n <div className=\"flex min-w-0 items-start gap-3\">\n <IconChip\n icon={enabled ? <ShieldCheck className=\"h-5 w-5\" /> : <ShieldOff className=\"h-5 w-5\" />}\n />\n <div className=\"min-w-0\">\n <p className=\"text-sm font-medium text-text-primary\">\n {enabled\n ? 'AI may read your health data'\n : 'AI processing is withdrawn — summaries will not run'}\n </p>\n <p className=\"mt-0.5 text-sm text-text-secondary\">\n {enabled\n ? 'The provider and model below are used to turn your logged training, nutrition, recovery and body-metric data into plain-language summaries.'\n : 'The provider and model below are saved, but no AI summary is generated while consent is withdrawn. Rule-based guidance and every manual logging path keep working.'}\n </p>\n {consent.data?.withdrawnAt && !enabled && (\n <p className=\"mt-1 text-xs text-text-tertiary\">\n Withdrawn {formatRelativeTime(consent.data.withdrawnAt)}.\n </p>\n )}\n {consent.data?.acceptedAt && enabled && (\n <p className=\"mt-1 text-xs text-text-tertiary\">\n Consent given {formatRelativeTime(consent.data.acceptedAt)}.\n </p>\n )}\n </div>\n </div>\n <Switch\n checked={enabled}\n onCheckedChange={(checked) => setConsent.mutate(checked)}\n disabled={setConsent.isPending}\n aria-label=\"Allow AI to read my health data\"\n />\n </div>\n </GlassCard>\n );\n}\n\ninterface AiControlsPageInnerProps {\n settings: MybAiSettings;\n logsQuery: UseQueryResult<MybAiOperationLogConnection, Error>;\n}\n\nfunction AiControlsPageInner({ settings, logsQuery }: AiControlsPageInnerProps) {\n const updateMutation = useMybUpdateAiSettings();\n const [form, setForm] = useState<AiSettingsForm>(() => makeFormFromSettings(settings));\n const [err, setErr] = useState<string | null>(null);\n const [saved, setSaved] = useState(false);\n\n const usagePercent = useMemo(() => {\n const { dailyAllowance, dailyAllowanceUsed } = settings;\n if (dailyAllowance <= 0) return 0;\n return Math.min(100, Math.round((dailyAllowanceUsed / dailyAllowance) * 100));\n }, [settings]);\n\n const isExhausted = settings.dailyAllowanceUsed >= settings.dailyAllowance;\n\n const handleProviderChange = (provider: MybAiProvider) => {\n setForm((f) => ({\n ...f,\n provider,\n modelName:\n f.modelName === DEFAULT_MODELS[f.provider] ? DEFAULT_MODELS[provider] : f.modelName,\n }));\n };\n\n const submit = async () => {\n setErr(null);\n setSaved(false);\n\n const allowance = Number(form.dailyAllowance);\n if (!Number.isInteger(allowance) || allowance < 0 || allowance > 10_000) {\n setErr('Daily allowance must be an integer between 0 and 10,000.');\n return;\n }\n if (!form.modelName.trim()) {\n setErr('Model name is required.');\n return;\n }\n\n const input: MybUpdateAiSettingsInput = {\n provider: form.provider,\n modelName: form.modelName.trim(),\n dailyAllowance: allowance,\n };\n\n if (form.clearKey) {\n input.providerKey = '';\n } else if (form.providerKey.trim()) {\n input.providerKey = form.providerKey.trim();\n }\n\n try {\n await updateMutation.mutateAsync(input);\n setSaved(true);\n setForm((f) => ({ ...f, providerKey: '', clearKey: false }));\n } catch (e) {\n setErr(e instanceof Error ? e.message : 'Failed to save AI settings.');\n }\n };\n\n const logColumns: Column<MybAiOperationLog>[] = [\n {\n key: 'createdAt',\n header: 'Time',\n sortValue: (r) => new Date(r.createdAt).getTime(),\n render: (r) => (\n <div className=\"text-text-primary\">\n <span className=\"block text-sm\">{formatDateTime(r.createdAt)}</span>\n <span className=\"text-xs text-text-tertiary\">{formatRelativeTime(r.createdAt)}</span>\n </div>\n ),\n },\n {\n key: 'operationType',\n header: 'Operation',\n render: (r) => (\n <span className=\"text-text-secondary\">{formatEnumLabel(r.operationType)}</span>\n ),\n },\n {\n key: 'provider',\n header: 'Provider / Model',\n render: (r) => (\n <div>\n <span className=\"block text-sm text-text-primary\">{PROVIDER_LABELS[r.provider]}</span>\n <span className=\"text-xs text-text-tertiary\">{r.modelName}</span>\n </div>\n ),\n },\n {\n key: 'tokens',\n header: 'Tokens',\n align: 'right',\n render: (r) => (\n <span className=\"tabular-nums text-text-secondary\">\n {r.inputTokens ?? '—'} / {r.outputTokens ?? '—'}\n </span>\n ),\n },\n {\n key: 'status',\n header: 'Status',\n render: (r) => (\n <span\n className={`inline-flex items-center rounded-full px-2 py-0.5 text-xs font-medium ${\n r.status === 'SUCCESS'\n ? 'bg-status-success-bg text-status-success-text'\n : 'bg-status-error-bg text-status-error-text'\n }`}\n >\n {formatEnumLabel(r.status)}\n </span>\n ),\n },\n {\n key: 'error',\n header: 'Error',\n hideOnMobile: true,\n render: (r) =>\n r.errorMessage ? (\n <span className=\"text-xs text-status-error-text\">{r.errorMessage}</span>\n ) : (\n <span className=\"text-text-tertiary\">—</span>\n ),\n },\n ];\n\n return (\n <PageLayout\n title=\"AI controls\"\n description=\"Choose your AI provider, model and daily allowance.\"\n actions={\n <Button className=\"gap-2\" onClick={submit} disabled={updateMutation.isPending}>\n {updateMutation.isPending ? <Spinner size=\"sm\" /> : <Save className=\"h-4 w-4\" />}\n Save settings\n </Button>\n }\n >\n {/* Consent gate — settings below only take effect while this is on. */}\n <Section\n title=\"AI-processing consent\"\n description=\"AI summaries only run while you allow AI to read your health data.\"\n >\n <AiConsentGate />\n </Section>\n\n {/* KPI grid */}\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-3\">\n <MetricsCard\n icon={<Sparkles className=\"h-5 w-5\" />}\n value={settings.provider}\n label=\"Current provider\"\n />\n <MetricsCard\n icon={<Bot className=\"h-5 w-5\" />}\n value={settings.modelName}\n label=\"Current model\"\n />\n <MetricsCard\n icon={<Activity className=\"h-5 w-5\" />}\n value={`${settings.dailyAllowanceUsed} / ${settings.dailyAllowance}`}\n label=\"Daily allowance used\"\n accent={isExhausted ? 'error' : usagePercent >= 80 ? 'warning' : 'success'}\n footer={\n <p className=\"text-xs text-text-tertiary\">\n Resets {formatRelativeTime(settings.allowanceResetAt)}\n </p>\n }\n />\n </div>\n\n {/* Settings form */}\n <Section title=\"Configuration\" description=\"Changes apply to your account only.\">\n <GlassCard treatment=\"plain\" className=\"p-6\">\n <div className=\"grid gap-6 md:grid-cols-2\">\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-ai-provider\">AI provider</Label>\n <Select\n value={form.provider}\n onValueChange={(v) => handleProviderChange(v as MybAiProvider)}\n >\n <SelectTrigger id=\"myb-ai-provider\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {AI_PROVIDERS.map((p) => (\n <SelectItem key={p} value={p}>\n {PROVIDER_LABELS[p]}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n <p className=\"text-xs text-text-tertiary\">\n The provider used for AI-generated guidance summaries.\n </p>\n </div>\n\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-ai-model\">Model name</Label>\n <Input\n id=\"myb-ai-model\"\n value={form.modelName}\n onChange={(e) => setForm((f) => ({ ...f, modelName: e.target.value }))}\n placeholder={DEFAULT_MODELS[form.provider]}\n />\n <p className=\"text-xs text-text-tertiary\">\n e.g. {DEFAULT_MODELS[form.provider]}. Must be valid for the chosen provider.\n </p>\n </div>\n\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-ai-allowance\">Daily allowance</Label>\n <Input\n id=\"myb-ai-allowance\"\n type=\"number\"\n min={0}\n max={10000}\n step={1}\n value={form.dailyAllowance}\n onChange={(e) => setForm((f) => ({ ...f, dailyAllowance: e.target.value }))}\n />\n <p className=\"text-xs text-text-tertiary\">\n Maximum AI operations counted against your account per UTC day.\n </p>\n </div>\n\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-ai-key\">\n <span className=\"inline-flex items-center gap-1.5\">\n <KeyRound className=\"h-3.5 w-3.5\" />\n Provider API key\n </span>\n </Label>\n <Input\n id=\"myb-ai-key\"\n type=\"password\"\n value={form.providerKey}\n onChange={(e) =>\n setForm((f) => ({ ...f, providerKey: e.target.value, clearKey: false }))\n }\n placeholder={settings.hasProviderKey ? '•••••••• (saved)' : 'Optional'}\n disabled={form.clearKey}\n />\n <div className=\"flex items-center gap-2\">\n <input\n id=\"myb-ai-clear-key\"\n type=\"checkbox\"\n checked={form.clearKey}\n onChange={(e) =>\n setForm((f) => ({ ...f, clearKey: e.target.checked, providerKey: '' }))\n }\n className=\"h-4 w-4 rounded border-border-seam text-accent focus:ring-accent\"\n />\n <Label\n htmlFor=\"myb-ai-clear-key\"\n className=\"text-xs font-normal text-text-secondary\"\n >\n Clear saved key and fall back to the workspace default\n </Label>\n </div>\n </div>\n </div>\n\n {saved && <p className=\"mt-4 text-sm text-status-success-text\">AI settings saved.</p>}\n {err && <p className=\"mt-4 text-sm text-status-error-text\">{err}</p>}\n </GlassCard>\n </Section>\n\n {/* Operation log */}\n <Section\n title=\"Operation history\"\n description=\"Redacted audit log of AI operations for your account.\"\n >\n {logsQuery.isLoading ? (\n <LoadingView label=\"Loading operation history…\" />\n ) : logsQuery.isError ? (\n <ErrorView\n message=\"We couldn't load the operation log.\"\n onRetry={() => logsQuery.refetch()}\n />\n ) : logsQuery.data?.items.length === 0 ? (\n <GlassCard treatment=\"plain\" className=\"p-8 text-center\">\n <Sparkles className=\"mx-auto h-8 w-8 text-text-tertiary\" />\n <p className=\"mt-3 text-sm font-medium text-text-primary\">No AI operations yet</p>\n <p className=\"mt-1 text-xs text-text-tertiary\">\n Operations appear here once AI-generated guidance runs.\n </p>\n </GlassCard>\n ) : (\n <DataTable\n columns={logColumns}\n rows={logsQuery.data?.items ?? []}\n rowKey={(r) => r.id}\n defaultSort={{ key: 'createdAt', dir: 'desc' }}\n />\n )}\n </Section>\n </PageLayout>\n );\n}\n\nexport function AiControlsPage() {\n const settingsQuery = useMybAiSettings();\n const logsQuery = useMybAiOperationLogs();\n\n if (settingsQuery.isLoading) {\n return (\n <PageLayout title=\"AI controls\">\n <LoadingView label=\"Loading AI settings…\" />\n </PageLayout>\n );\n }\n\n if (settingsQuery.isError) {\n return (\n <PageLayout title=\"AI controls\">\n <ErrorView\n title=\"Could not load AI settings\"\n message=\"We couldn't fetch your AI configuration. Try again in a moment.\"\n onRetry={() => settingsQuery.refetch()}\n />\n </PageLayout>\n );\n }\n\n const settings = settingsQuery.data;\n if (!settings) {\n return (\n <PageLayout title=\"AI controls\">\n <ErrorView\n title=\"Could not load AI settings\"\n message=\"We couldn't fetch your AI configuration. Try again in a moment.\"\n onRetry={() => settingsQuery.refetch()}\n />\n </PageLayout>\n );\n }\n\n // Key the inner component on the settings revision so form state re-seeds\n // when the source settings change, without synchronously setting state in\n // an effect (which triggers cascading renders).\n const settingsKey = `${settings.id}:${settings.updatedAt}`;\n\n return <AiControlsPageInner key={settingsKey} settings={settings} logsQuery={logsQuery} />;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"AiControlsPage.js","names":[],"sources":["../../../../src/mybodyshield/pages/ai/AiControlsPage.tsx"],"sourcesContent":["/**\n * AI controls — provider, model, daily allowance and operation history.\n * @module mybodyshield/pages/ai\n *\n * BOFF-5949: per-member AI settings surface. Lets the member choose a provider\n * (Gemini / OpenAI / Anthropic), the model name, a daily operation allowance,\n * and an optional provider API key. Shows the redacted audit log of AI ops.\n */\n\nimport { useMemo, useState } from 'react';\nimport { Sparkles, KeyRound, Bot, Activity, Save, ShieldCheck, ShieldOff } from 'lucide-react';\nimport {\n Button,\n Input,\n Label,\n Select,\n SelectTrigger,\n SelectContent,\n SelectItem,\n SelectValue,\n Switch,\n GlassCard,\n Spinner,\n} from '@burdenoff/fe-libs/ui';\nimport {\n PageLayout,\n MetricsCard,\n LoadingView,\n ErrorView,\n Section,\n DataTable,\n type Column,\n} from '../../components';\nimport {\n useMybAiSettings,\n useMybUpdateAiSettings,\n useMybAiOperationLogs,\n useMybAiProcessingConsent,\n useMybSetAiProcessingConsent,\n useTr,\n} from '../../hooks';\nimport type {\n MybAiProvider,\n MybAiSettings,\n MybAiOperationLog,\n MybAiOperationLogConnection,\n MybUpdateAiSettingsInput,\n} from '../../types';\nimport { formatDateTime, formatRelativeTime, formatEnumLabel } from '../../utils/formatters';\nimport { IconChip } from '../_shared/visuals';\nimport type { UseQueryResult } from '@tanstack/react-query';\n\nconst AI_PROVIDERS: MybAiProvider[] = ['GEMINI', 'OPENAI', 'ANTHROPIC'];\n\nconst PROVIDER_LABELS: Record<MybAiProvider, string> = {\n GEMINI: 'Google Gemini',\n OPENAI: 'OpenAI',\n ANTHROPIC: 'Anthropic',\n};\n\nconst DEFAULT_MODELS: Record<MybAiProvider, string> = {\n GEMINI: 'gemini-2.5-flash',\n OPENAI: 'gpt-4o-mini',\n ANTHROPIC: 'claude-3-5-haiku-latest',\n};\n\ninterface AiSettingsForm {\n provider: MybAiProvider;\n modelName: string;\n dailyAllowance: string;\n providerKey: string;\n clearKey: boolean;\n}\n\nconst makeFormFromSettings = (settings: MybAiSettings): AiSettingsForm => ({\n provider: settings.provider,\n modelName: settings.modelName,\n dailyAllowance: String(settings.dailyAllowance),\n providerKey: '',\n clearKey: false,\n});\n\n/**\n * AI-processing consent gate (BOFF-3838).\n *\n * The settings on this page only take effect while consent is granted: the\n * service checks `mybMyAiProcessingConsent` before it will generate a guidance\n * summary, and returns a null summary without it. Surfacing the state here (and\n * not only under Settings) stops a member from configuring a provider and model\n * that can never run. Reads and writes the same query key as the Settings page,\n * so the two stay in sync.\n */\nfunction AiConsentGate() {\n const t = useTr();\n const consent = useMybAiProcessingConsent();\n const setConsent = useMybSetAiProcessingConsent();\n\n if (consent.isLoading) {\n return (\n <LoadingView\n label={t('mybodyshield.pages.aiControls.loadingConsent', 'Loading AI-processing consent…')}\n />\n );\n }\n\n if (consent.isError) {\n return (\n <ErrorView\n message={t(\n 'mybodyshield.pages.aiControls.consentError',\n \"We couldn't load your AI-processing consent.\"\n )}\n onRetry={() => consent.refetch()}\n />\n );\n }\n\n const enabled = consent.data?.enabled ?? false;\n\n return (\n <GlassCard treatment=\"plain\" className=\"p-5\">\n <div className=\"flex flex-wrap items-start justify-between gap-4\">\n <div className=\"flex min-w-0 items-start gap-3\">\n <IconChip\n icon={enabled ? <ShieldCheck className=\"h-5 w-5\" /> : <ShieldOff className=\"h-5 w-5\" />}\n />\n <div className=\"min-w-0\">\n <p className=\"text-sm font-medium text-text-primary\">\n {enabled\n ? t('mybodyshield.pages.aiControls.aiMayReadData', 'AI may read your health data')\n : t(\n 'mybodyshield.pages.aiControls.aiWithdrawn',\n 'AI processing is withdrawn — summaries will not run'\n )}\n </p>\n <p className=\"mt-0.5 text-sm text-text-secondary\">\n {enabled\n ? t(\n 'mybodyshield.pages.aiControls.aiEnabledDescription',\n 'The provider and model below are used to turn your logged training, nutrition, recovery and body-metric data into plain-language summaries.'\n )\n : t(\n 'mybodyshield.pages.aiControls.aiWithdrawnDescription',\n 'The provider and model below are saved, but no AI summary is generated while consent is withdrawn. Rule-based guidance and every manual logging path keep working.'\n )}\n </p>\n {consent.data?.withdrawnAt && !enabled && (\n <p className=\"mt-1 text-xs text-text-tertiary\">\n {t('mybodyshield.pages.aiControls.withdrawnAgo', 'Withdrawn {{time}}.', {\n time: formatRelativeTime(consent.data.withdrawnAt),\n })}\n </p>\n )}\n {consent.data?.acceptedAt && enabled && (\n <p className=\"mt-1 text-xs text-text-tertiary\">\n {t('mybodyshield.pages.aiControls.consentGivenAgo', 'Consent given {{time}}.', {\n time: formatRelativeTime(consent.data.acceptedAt),\n })}\n </p>\n )}\n </div>\n </div>\n <Switch\n checked={enabled}\n onCheckedChange={(checked) => setConsent.mutate(checked)}\n disabled={setConsent.isPending}\n aria-label={t(\n 'mybodyshield.pages.aiControls.allowAiData',\n 'Allow AI to read my health data'\n )}\n />\n </div>\n </GlassCard>\n );\n}\n\ninterface AiControlsPageInnerProps {\n settings: MybAiSettings;\n logsQuery: UseQueryResult<MybAiOperationLogConnection, Error>;\n}\n\nfunction AiControlsPageInner({ settings, logsQuery }: AiControlsPageInnerProps) {\n const t = useTr();\n const updateMutation = useMybUpdateAiSettings();\n const [form, setForm] = useState<AiSettingsForm>(() => makeFormFromSettings(settings));\n const [err, setErr] = useState<string | null>(null);\n const [saved, setSaved] = useState(false);\n\n const usagePercent = useMemo(() => {\n const { dailyAllowance, dailyAllowanceUsed } = settings;\n if (dailyAllowance <= 0) return 0;\n return Math.min(100, Math.round((dailyAllowanceUsed / dailyAllowance) * 100));\n }, [settings]);\n\n const isExhausted = settings.dailyAllowanceUsed >= settings.dailyAllowance;\n\n const handleProviderChange = (provider: MybAiProvider) => {\n setForm((f) => ({\n ...f,\n provider,\n modelName:\n f.modelName === DEFAULT_MODELS[f.provider] ? DEFAULT_MODELS[provider] : f.modelName,\n }));\n };\n\n const submit = async () => {\n setErr(null);\n setSaved(false);\n\n const allowance = Number(form.dailyAllowance);\n if (!Number.isInteger(allowance) || allowance < 0 || allowance > 10_000) {\n setErr(\n t(\n 'mybodyshield.pages.aiControls.allowanceRange',\n 'Daily allowance must be an integer between 0 and 10,000.'\n )\n );\n return;\n }\n if (!form.modelName.trim()) {\n setErr(t('mybodyshield.pages.aiControls.modelRequired', 'Model name is required.'));\n return;\n }\n\n const input: MybUpdateAiSettingsInput = {\n provider: form.provider,\n modelName: form.modelName.trim(),\n dailyAllowance: allowance,\n };\n\n if (form.clearKey) {\n input.providerKey = '';\n } else if (form.providerKey.trim()) {\n input.providerKey = form.providerKey.trim();\n }\n\n try {\n await updateMutation.mutateAsync(input);\n setSaved(true);\n setForm((f) => ({ ...f, providerKey: '', clearKey: false }));\n } catch (e) {\n setErr(\n e instanceof Error\n ? e.message\n : t('mybodyshield.pages.aiControls.failedToSave', 'Failed to save AI settings.')\n );\n }\n };\n\n const logColumns: Column<MybAiOperationLog>[] = [\n {\n key: 'createdAt',\n header: t('mybodyshield.pages.aiControls.time', 'Time'),\n sortValue: (r) => new Date(r.createdAt).getTime(),\n render: (r) => (\n <div className=\"text-text-primary\">\n <span className=\"block text-sm\">{formatDateTime(r.createdAt)}</span>\n <span className=\"text-xs text-text-tertiary\">{formatRelativeTime(r.createdAt)}</span>\n </div>\n ),\n },\n {\n key: 'operationType',\n header: t('mybodyshield.pages.aiControls.operation', 'Operation'),\n render: (r) => (\n <span className=\"text-text-secondary\">{formatEnumLabel(r.operationType)}</span>\n ),\n },\n {\n key: 'provider',\n header: t('mybodyshield.pages.aiControls.providerModel', 'Provider / Model'),\n render: (r) => (\n <div>\n <span className=\"block text-sm text-text-primary\">{PROVIDER_LABELS[r.provider]}</span>\n <span className=\"text-xs text-text-tertiary\">{r.modelName}</span>\n </div>\n ),\n },\n {\n key: 'tokens',\n header: t('mybodyshield.pages.aiControls.tokens', 'Tokens'),\n align: 'right',\n render: (r) => (\n <span className=\"tabular-nums text-text-secondary\">\n {r.inputTokens ?? '—'} / {r.outputTokens ?? '—'}\n </span>\n ),\n },\n {\n key: 'status',\n header: t('mybodyshield.pages.aiControls.status', 'Status'),\n render: (r) => (\n <span\n className={`inline-flex items-center rounded-full px-2 py-0.5 text-xs font-medium ${\n r.status === 'SUCCESS'\n ? 'bg-status-success-bg text-status-success-text'\n : 'bg-status-error-bg text-status-error-text'\n }`}\n >\n {formatEnumLabel(r.status)}\n </span>\n ),\n },\n {\n key: 'error',\n header: t('mybodyshield.pages.aiControls.errorColumn', 'Error'),\n hideOnMobile: true,\n render: (r) =>\n r.errorMessage ? (\n <span className=\"text-xs text-status-error-text\">{r.errorMessage}</span>\n ) : (\n <span className=\"text-text-tertiary\">—</span>\n ),\n },\n ];\n\n return (\n <PageLayout\n title={t('mybodyshield.pages.aiControls.title', 'AI controls')}\n description={t(\n 'mybodyshield.pages.aiControls.description',\n 'Choose your AI provider, model and daily allowance.'\n )}\n actions={\n <Button className=\"gap-2\" onClick={submit} disabled={updateMutation.isPending}>\n {updateMutation.isPending ? <Spinner size=\"sm\" /> : <Save className=\"h-4 w-4\" />}\n {t('mybodyshield.pages.aiControls.saveSettings', 'Save settings')}\n </Button>\n }\n >\n {/* Consent gate — settings below only take effect while this is on. */}\n <Section\n title={t('mybodyshield.pages.aiControls.consentTitle', 'AI-processing consent')}\n description={t(\n 'mybodyshield.pages.aiControls.consentDescription',\n 'AI summaries only run while you allow AI to read your health data.'\n )}\n >\n <AiConsentGate />\n </Section>\n\n {/* KPI grid */}\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-3\">\n <MetricsCard\n icon={<Sparkles className=\"h-5 w-5\" />}\n value={settings.provider}\n label={t('mybodyshield.pages.aiControls.currentProvider', 'Current provider')}\n />\n <MetricsCard\n icon={<Bot className=\"h-5 w-5\" />}\n value={settings.modelName}\n label={t('mybodyshield.pages.aiControls.currentModel', 'Current model')}\n />\n <MetricsCard\n icon={<Activity className=\"h-5 w-5\" />}\n value={`${settings.dailyAllowanceUsed} / ${settings.dailyAllowance}`}\n label={t('mybodyshield.pages.aiControls.dailyAllowanceUsed', 'Daily allowance used')}\n accent={isExhausted ? 'error' : usagePercent >= 80 ? 'warning' : 'success'}\n footer={\n <p className=\"text-xs text-text-tertiary\">\n {t('mybodyshield.pages.aiControls.resets', 'Resets {{time}}', {\n time: formatRelativeTime(settings.allowanceResetAt),\n })}\n </p>\n }\n />\n </div>\n\n {/* Settings form */}\n <Section\n title={t('mybodyshield.pages.aiControls.configuration', 'Configuration')}\n description={t(\n 'mybodyshield.pages.aiControls.configurationDescription',\n 'Changes apply to your account only.'\n )}\n >\n <GlassCard treatment=\"plain\" className=\"p-6\">\n <div className=\"grid gap-6 md:grid-cols-2\">\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-ai-provider\">\n {t('mybodyshield.pages.aiControls.aiProvider', 'AI provider')}\n </Label>\n <Select\n value={form.provider}\n onValueChange={(v) => handleProviderChange(v as MybAiProvider)}\n >\n <SelectTrigger id=\"myb-ai-provider\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {AI_PROVIDERS.map((p) => (\n <SelectItem key={p} value={p}>\n {PROVIDER_LABELS[p]}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n <p className=\"text-xs text-text-tertiary\">\n {t(\n 'mybodyshield.pages.aiControls.providerDescription',\n 'The provider used for AI-generated guidance summaries.'\n )}\n </p>\n </div>\n\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-ai-model\">\n {t('mybodyshield.pages.aiControls.modelName', 'Model name')}\n </Label>\n <Input\n id=\"myb-ai-model\"\n value={form.modelName}\n onChange={(e) => setForm((f) => ({ ...f, modelName: e.target.value }))}\n placeholder={t('mybodyshield.pages.aiControls.modelPlaceholder', 'e.g. {{model}}', {\n model: DEFAULT_MODELS[form.provider],\n })}\n />\n <p className=\"text-xs text-text-tertiary\">\n {t(\n 'mybodyshield.pages.aiControls.modelHint',\n 'Must be valid for the chosen provider.'\n )}\n </p>\n </div>\n\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-ai-allowance\">\n {t('mybodyshield.pages.aiControls.dailyAllowance', 'Daily allowance')}\n </Label>\n <Input\n id=\"myb-ai-allowance\"\n type=\"number\"\n min={0}\n max={10000}\n step={1}\n value={form.dailyAllowance}\n onChange={(e) => setForm((f) => ({ ...f, dailyAllowance: e.target.value }))}\n />\n <p className=\"text-xs text-text-tertiary\">\n {t(\n 'mybodyshield.pages.aiControls.dailyAllowanceHint',\n 'Maximum AI operations counted against your account per UTC day.'\n )}\n </p>\n </div>\n\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"myb-ai-key\">\n <span className=\"inline-flex items-center gap-1.5\">\n <KeyRound className=\"h-3.5 w-3.5\" />\n {t('mybodyshield.pages.aiControls.providerApiKey', 'Provider API key')}\n </span>\n </Label>\n <Input\n id=\"myb-ai-key\"\n type=\"password\"\n value={form.providerKey}\n onChange={(e) =>\n setForm((f) => ({ ...f, providerKey: e.target.value, clearKey: false }))\n }\n placeholder={\n settings.hasProviderKey\n ? t('mybodyshield.pages.aiControls.providerKeySaved', '•••••••• (saved)')\n : t('mybodyshield.pages.aiControls.providerKeyPlaceholder', 'Optional')\n }\n disabled={form.clearKey}\n />\n <div className=\"flex items-center gap-2\">\n <input\n id=\"myb-ai-clear-key\"\n type=\"checkbox\"\n checked={form.clearKey}\n onChange={(e) =>\n setForm((f) => ({ ...f, clearKey: e.target.checked, providerKey: '' }))\n }\n className=\"h-4 w-4 rounded border-border-seam text-accent focus:ring-accent\"\n />\n <Label\n htmlFor=\"myb-ai-clear-key\"\n className=\"text-xs font-normal text-text-secondary\"\n >\n {t(\n 'mybodyshield.pages.aiControls.clearKey',\n 'Clear saved key and fall back to the workspace default'\n )}\n </Label>\n </div>\n </div>\n </div>\n\n {saved && (\n <p className=\"mt-4 text-sm text-status-success-text\">\n {t('mybodyshield.pages.aiControls.settingsSaved', 'AI settings saved.')}\n </p>\n )}\n {err && <p className=\"mt-4 text-sm text-status-error-text\">{err}</p>}\n </GlassCard>\n </Section>\n\n {/* Operation log */}\n <Section\n title={t('mybodyshield.pages.aiControls.operationHistory', 'Operation history')}\n description={t(\n 'mybodyshield.pages.aiControls.operationHistoryDescription',\n 'Redacted audit log of AI operations for your account.'\n )}\n >\n {logsQuery.isLoading ? (\n <LoadingView\n label={t('mybodyshield.pages.aiControls.loadingHistory', 'Loading operation history…')}\n />\n ) : logsQuery.isError ? (\n <ErrorView\n message={t(\n 'mybodyshield.pages.aiControls.historyError',\n \"We couldn't load the operation log.\"\n )}\n onRetry={() => logsQuery.refetch()}\n />\n ) : logsQuery.data?.items.length === 0 ? (\n <GlassCard treatment=\"plain\" className=\"p-8 text-center\">\n <Sparkles className=\"mx-auto h-8 w-8 text-text-tertiary\" />\n <p className=\"mt-3 text-sm font-medium text-text-primary\">\n {t('mybodyshield.pages.aiControls.noOperations', 'No AI operations yet')}\n </p>\n <p className=\"mt-1 text-xs text-text-tertiary\">\n {t(\n 'mybodyshield.pages.aiControls.noOperationsDescription',\n 'Operations appear here once AI-generated guidance runs.'\n )}\n </p>\n </GlassCard>\n ) : (\n <DataTable\n columns={logColumns}\n rows={logsQuery.data?.items ?? []}\n rowKey={(r) => r.id}\n defaultSort={{ key: 'createdAt', dir: 'desc' }}\n />\n )}\n </Section>\n </PageLayout>\n );\n}\n\nexport function AiControlsPage() {\n const t = useTr();\n const settingsQuery = useMybAiSettings();\n const logsQuery = useMybAiOperationLogs();\n\n if (settingsQuery.isLoading) {\n return (\n <PageLayout title={t('mybodyshield.pages.aiControls.title', 'AI controls')}>\n <LoadingView\n label={t('mybodyshield.pages.aiControls.loadingSettings', 'Loading AI settings…')}\n />\n </PageLayout>\n );\n }\n\n if (settingsQuery.isError) {\n return (\n <PageLayout title={t('mybodyshield.pages.aiControls.title', 'AI controls')}>\n <ErrorView\n title={t('mybodyshield.pages.aiControls.errorTitle', 'Could not load AI settings')}\n message={t(\n 'mybodyshield.pages.aiControls.error',\n \"We couldn't fetch your AI configuration. Try again in a moment.\"\n )}\n onRetry={() => settingsQuery.refetch()}\n />\n </PageLayout>\n );\n }\n\n const settings = settingsQuery.data;\n if (!settings) {\n return (\n <PageLayout title={t('mybodyshield.pages.aiControls.title', 'AI controls')}>\n <ErrorView\n title={t('mybodyshield.pages.aiControls.errorTitle', 'Could not load AI settings')}\n message={t(\n 'mybodyshield.pages.aiControls.error',\n \"We couldn't fetch your AI configuration. Try again in a moment.\"\n )}\n onRetry={() => settingsQuery.refetch()}\n />\n </PageLayout>\n );\n }\n\n // Key the inner component on the settings revision so form state re-seeds\n // when the source settings change, without synchronously setting state in\n // an effect (which triggers cascading renders).\n const settingsKey = `${settings.id}:${settings.updatedAt}`;\n\n return <AiControlsPageInner key={settingsKey} settings={settings} logsQuery={logsQuery} />;\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"CoachingPage.d.ts","sourceRoot":"","sources":["../../../../src/mybodyshield/pages/coaching/CoachingPage.tsx"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAinBH,wBAAgB,YAAY,gCA4I3B"}
1
+ {"version":3,"file":"CoachingPage.d.ts","sourceRoot":"","sources":["../../../../src/mybodyshield/pages/coaching/CoachingPage.tsx"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AA+wBH,wBAAgB,YAAY,gCAoK3B"}