@burdenoff/microfe-brainyrich 2026.1101.2 → 2026.1101.3

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 (65) hide show
  1. package/dist/brainyrich/BrainyRichRoot.d.ts.map +1 -1
  2. package/dist/brainyrich/BrainyRichRoot.js +22 -107
  3. package/dist/brainyrich/BrainyRichRoot.js.map +1 -1
  4. package/dist/brainyrich/BrainyRichRoutes.d.ts.map +1 -1
  5. package/dist/brainyrich/BrainyRichRoutes.js +65 -46
  6. package/dist/brainyrich/BrainyRichRoutes.js.map +1 -1
  7. package/dist/brainyrich/components/mobile/MobilePage.d.ts +44 -0
  8. package/dist/brainyrich/components/mobile/MobilePage.d.ts.map +1 -0
  9. package/dist/brainyrich/components/mobile/MobilePage.js +45 -0
  10. package/dist/brainyrich/components/mobile/MobilePage.js.map +1 -0
  11. package/dist/brainyrich/components/mobile/MobileTabBar.d.ts +20 -0
  12. package/dist/brainyrich/components/mobile/MobileTabBar.d.ts.map +1 -0
  13. package/dist/brainyrich/components/mobile/MobileTabBar.js +139 -0
  14. package/dist/brainyrich/components/mobile/MobileTabBar.js.map +1 -0
  15. package/dist/brainyrich/components/mobile/index.d.ts +8 -0
  16. package/dist/brainyrich/components/mobile/index.d.ts.map +1 -0
  17. package/dist/brainyrich/components/mobile/index.js +2 -0
  18. package/dist/brainyrich/hooks/useBrainyRichApi.d.ts +48 -12
  19. package/dist/brainyrich/hooks/useBrainyRichApi.d.ts.map +1 -1
  20. package/dist/brainyrich/hooks/useBrainyRichApi.js +120 -108
  21. package/dist/brainyrich/hooks/useBrainyRichApi.js.map +1 -1
  22. package/dist/brainyrich/hooks/useIsMobileViewport.d.ts +14 -0
  23. package/dist/brainyrich/hooks/useIsMobileViewport.d.ts.map +1 -0
  24. package/dist/brainyrich/hooks/useIsMobileViewport.js +23 -0
  25. package/dist/brainyrich/hooks/useIsMobileViewport.js.map +1 -0
  26. package/dist/brainyrich/navItems.d.ts +36 -0
  27. package/dist/brainyrich/navItems.d.ts.map +1 -0
  28. package/dist/brainyrich/navItems.js +54 -0
  29. package/dist/brainyrich/navItems.js.map +1 -0
  30. package/dist/brainyrich/pages/AccountsPage.d.ts.map +1 -1
  31. package/dist/brainyrich/pages/AccountsPage.js +411 -390
  32. package/dist/brainyrich/pages/AccountsPage.js.map +1 -1
  33. package/dist/brainyrich/pages/DashboardPage.d.ts.map +1 -1
  34. package/dist/brainyrich/pages/DashboardPage.js +220 -174
  35. package/dist/brainyrich/pages/DashboardPage.js.map +1 -1
  36. package/dist/brainyrich/pages/GoalsPage.d.ts.map +1 -1
  37. package/dist/brainyrich/pages/GoalsPage.js +560 -551
  38. package/dist/brainyrich/pages/GoalsPage.js.map +1 -1
  39. package/dist/brainyrich/pages/mobile/MobileAccountsView.d.ts +28 -0
  40. package/dist/brainyrich/pages/mobile/MobileAccountsView.d.ts.map +1 -0
  41. package/dist/brainyrich/pages/mobile/MobileAccountsView.js +139 -0
  42. package/dist/brainyrich/pages/mobile/MobileAccountsView.js.map +1 -0
  43. package/dist/brainyrich/pages/mobile/MobileDashboardView.d.ts +24 -0
  44. package/dist/brainyrich/pages/mobile/MobileDashboardView.d.ts.map +1 -0
  45. package/dist/brainyrich/pages/mobile/MobileDashboardView.js +245 -0
  46. package/dist/brainyrich/pages/mobile/MobileDashboardView.js.map +1 -0
  47. package/dist/brainyrich/pages/mobile/MobileGoalsView.d.ts +30 -0
  48. package/dist/brainyrich/pages/mobile/MobileGoalsView.d.ts.map +1 -0
  49. package/dist/brainyrich/pages/mobile/MobileGoalsView.js +180 -0
  50. package/dist/brainyrich/pages/mobile/MobileGoalsView.js.map +1 -0
  51. package/dist/brainyrich/pages/mobile/mobileShared.d.ts +24 -0
  52. package/dist/brainyrich/pages/mobile/mobileShared.d.ts.map +1 -0
  53. package/dist/brainyrich/pages/mobile/mobileShared.js +25 -0
  54. package/dist/brainyrich/pages/mobile/mobileShared.js.map +1 -0
  55. package/dist/brainyrich/utils/cn.js +10 -0
  56. package/dist/brainyrich/utils/cn.js.map +1 -0
  57. package/dist/brainyrich/utils/goalMetrics.d.ts +13 -0
  58. package/dist/brainyrich/utils/goalMetrics.d.ts.map +1 -0
  59. package/dist/brainyrich/utils/goalMetrics.js +20 -0
  60. package/dist/brainyrich/utils/goalMetrics.js.map +1 -0
  61. package/dist/test/brainyrichFixtures.d.ts +7 -0
  62. package/dist/test/brainyrichFixtures.d.ts.map +1 -0
  63. package/dist/test/viewport.d.ts +8 -0
  64. package/dist/test/viewport.d.ts.map +1 -0
  65. package/package.json +1 -1
@@ -0,0 +1,245 @@
1
+ import { useBrainyRich as e } from "../../providers/BrainyRichProvider.js";
2
+ import { DonutChart as t, GlassCard as n, ProgressRing as r, useVizColors as i } from "../../components/viz.js";
3
+ import { EmptyState as a, ErrorState as o, LoadingState as s, formatCompact as c, formatDate as l, formatMoney as u, formatPct as d, statusBadgeVariant as f } from "../_shared.js";
4
+ import { cn as p } from "../../utils/cn.js";
5
+ import { goalDetailPath as m, sectionPath as h } from "../../navItems.js";
6
+ import { MobilePage as g } from "../../components/mobile/MobilePage.js";
7
+ import "../../components/mobile/index.js";
8
+ import { fundedPercent as _, goalTone as v } from "../../utils/goalMetrics.js";
9
+ import { MobileCard as y, projectionChipClass as b } from "./mobileShared.js";
10
+ import { ChevronRight as x, CircleCheck as S, Coins as C, Target as w, TrendingUp as T, Wallet as E } from "lucide-react";
11
+ import { useNavigate as D } from "react-router";
12
+ import { Fragment as O, jsx as k, jsxs as A } from "react/jsx-runtime";
13
+ import { Badge as j } from "@burdenoff/fe-libs/ui";
14
+ //#region src/brainyrich/pages/mobile/MobileDashboardView.tsx
15
+ function M(e) {
16
+ let t = e.replace(/_/g, " ").toLowerCase();
17
+ return t.charAt(0).toUpperCase() + t.slice(1);
18
+ }
19
+ function N({ translate: N, summary: P, allocation: F, insights: I, goals: L, portfolios: R, renderInsight: z, newAccountAction: B }) {
20
+ let V = D(), { basePath: H = "/" } = e(), U = i(), W = P.data, G = W?.currency ?? "USD", K = F.reduce((e, t) => e + t.value, 0), q = I.data.slice(0, 3), J = L.data.slice(0, 8), Y = R.data.slice(0, 3), X = (e, t) => /* @__PURE__ */ A("button", {
21
+ type: "button",
22
+ onClick: () => V(t),
23
+ className: "inline-flex min-h-11 items-center gap-0.5 text-xs font-medium text-text-secondary hover:text-text-primary",
24
+ children: [e, /* @__PURE__ */ k(x, {
25
+ className: "size-3.5",
26
+ "aria-hidden": "true"
27
+ })]
28
+ });
29
+ return /* @__PURE__ */ A(g, {
30
+ tourId: "brainyrich-page-dashboard",
31
+ title: N("brainyrich.dashboard.title", "Wealth Dashboard"),
32
+ subtitle: N("brainyrich.mobile.dashboard.subtitle", "Your wealth at a glance"),
33
+ brandMark: !0,
34
+ action: B,
35
+ children: [
36
+ P.isLoading ? /* @__PURE__ */ k(s, { rows: 2 }) : P.isError ? /* @__PURE__ */ k(o, {
37
+ title: N("brainyrich.dashboard.netWorthError", "Could not load net worth"),
38
+ onRetry: P.refetch
39
+ }) : W ? /* @__PURE__ */ A(O, { children: [/* @__PURE__ */ A(n, {
40
+ glow: !0,
41
+ className: "p-5",
42
+ children: [
43
+ /* @__PURE__ */ A("div", {
44
+ className: "flex items-start justify-between gap-2",
45
+ children: [/* @__PURE__ */ k("p", {
46
+ className: "text-sm text-text-secondary",
47
+ children: N("brainyrich.dashboard.metrics.netWorth", "Net Worth")
48
+ }), /* @__PURE__ */ k(E, {
49
+ className: "size-4 text-text-secondary",
50
+ "aria-hidden": "true"
51
+ })]
52
+ }),
53
+ /* @__PURE__ */ k("p", {
54
+ className: "mt-1 text-4xl font-semibold tracking-tight text-text-primary",
55
+ "data-testid": "brainyrich-mobile-networth",
56
+ children: c(W.totalNetWorth, G)
57
+ }),
58
+ /* @__PURE__ */ k("p", {
59
+ className: "mt-1 text-xs text-text-secondary",
60
+ children: N("brainyrich.mobile.dashboard.accountsLinked", "{{count}} linked accounts", { count: W.accountCount })
61
+ })
62
+ ]
63
+ }), /* @__PURE__ */ A("div", {
64
+ className: "grid grid-cols-2 gap-3",
65
+ children: [/* @__PURE__ */ A(y, { children: [/* @__PURE__ */ A("div", {
66
+ className: "flex items-center gap-1.5 text-xs text-text-secondary",
67
+ children: [/* @__PURE__ */ k(T, {
68
+ className: "size-3.5",
69
+ "aria-hidden": "true"
70
+ }), N("brainyrich.mobile.dashboard.invested", "Invested")]
71
+ }), /* @__PURE__ */ k("p", {
72
+ className: "mt-1 text-xl font-semibold text-text-primary",
73
+ children: c(W.totalMarketValue, G)
74
+ })] }), /* @__PURE__ */ A(y, { children: [/* @__PURE__ */ A("div", {
75
+ className: "flex items-center gap-1.5 text-xs text-text-secondary",
76
+ children: [/* @__PURE__ */ k(C, {
77
+ className: "size-3.5",
78
+ "aria-hidden": "true"
79
+ }), N("brainyrich.mobile.dashboard.cash", "Cash")]
80
+ }), /* @__PURE__ */ k("p", {
81
+ className: "mt-1 text-xl font-semibold text-text-primary",
82
+ children: c(W.totalCash, G)
83
+ })] })]
84
+ })] }) : /* @__PURE__ */ k(a, {
85
+ icon: /* @__PURE__ */ k(E, {
86
+ className: "size-6",
87
+ "aria-hidden": "true"
88
+ }),
89
+ title: N("brainyrich.dashboard.noSummary", "No financial data yet"),
90
+ description: N("brainyrich.dashboard.noSummaryDesc", "Link your first account to see your net worth here.")
91
+ }),
92
+ !P.isLoading && !P.isError && F.length > 0 ? /* @__PURE__ */ A(y, { children: [/* @__PURE__ */ k("h2", {
93
+ className: "text-sm font-semibold text-text-primary",
94
+ children: N("brainyrich.dashboard.sections.positiveAssetAllocation", "Asset allocation")
95
+ }), /* @__PURE__ */ A("div", {
96
+ className: "mt-3 flex items-center gap-4",
97
+ children: [/* @__PURE__ */ k("div", {
98
+ className: "w-28 shrink-0",
99
+ children: /* @__PURE__ */ k(t, {
100
+ data: F,
101
+ legend: !1,
102
+ height: 112,
103
+ valueFormatter: (e) => u(e, G)
104
+ })
105
+ }), /* @__PURE__ */ k("ul", {
106
+ className: "min-w-0 flex-1 space-y-1.5",
107
+ "aria-label": N("brainyrich.dashboard.allocationLegend", "Allocation legend"),
108
+ children: F.map((e, t) => /* @__PURE__ */ A("li", {
109
+ className: "flex items-center gap-2 text-xs",
110
+ children: [
111
+ /* @__PURE__ */ k("span", {
112
+ "aria-hidden": "true",
113
+ className: "size-2 shrink-0 rounded-full",
114
+ style: { background: e.tone ? U.tone(e.tone) : U.palette[t % U.palette.length] }
115
+ }),
116
+ /* @__PURE__ */ k("span", {
117
+ className: "truncate text-text-secondary",
118
+ children: e.label
119
+ }),
120
+ /* @__PURE__ */ k("span", {
121
+ className: "ml-auto font-medium tabular-nums text-text-primary",
122
+ children: d(K > 0 ? e.value / K * 100 : 0, 0)
123
+ })
124
+ ]
125
+ }, e.label))
126
+ })]
127
+ })] }) : null,
128
+ /* @__PURE__ */ A("section", {
129
+ "aria-labelledby": "br-mobile-attention",
130
+ className: "space-y-2",
131
+ children: [/* @__PURE__ */ k("h2", {
132
+ id: "br-mobile-attention",
133
+ className: "text-sm font-semibold text-text-primary",
134
+ children: N("brainyrich.dashboard.sections.needsAttention", "Needs attention")
135
+ }), I.isLoading ? /* @__PURE__ */ k(s, { rows: 2 }) : I.isError ? /* @__PURE__ */ k(o, { onRetry: I.refetch }) : q.length > 0 ? /* @__PURE__ */ k("div", {
136
+ className: "space-y-2",
137
+ children: q.map((e) => /* @__PURE__ */ k("div", { children: z(e) }, e.id))
138
+ }) : /* @__PURE__ */ A(y, {
139
+ className: "flex items-center gap-3",
140
+ children: [/* @__PURE__ */ k(S, {
141
+ className: "size-5 text-status-success-text",
142
+ "aria-hidden": "true"
143
+ }), /* @__PURE__ */ k("p", {
144
+ className: "text-sm text-text-secondary",
145
+ children: N("brainyrich.dashboard.noInsightsDesc", "There are no open insights that need your attention.")
146
+ })]
147
+ })]
148
+ }),
149
+ /* @__PURE__ */ A("section", {
150
+ "aria-labelledby": "br-mobile-goals",
151
+ className: "space-y-2",
152
+ children: [/* @__PURE__ */ A("div", {
153
+ className: "flex items-center justify-between",
154
+ children: [/* @__PURE__ */ k("h2", {
155
+ id: "br-mobile-goals",
156
+ className: "text-sm font-semibold text-text-primary",
157
+ children: N("brainyrich.dashboard.sections.goals", "Goals on track")
158
+ }), J.length > 0 ? X(N("brainyrich.mobile.seeAll", "See all"), h(H, "brainyrich-goals")) : null]
159
+ }), L.isLoading ? /* @__PURE__ */ k(s, { rows: 1 }) : L.isError ? /* @__PURE__ */ k(o, { onRetry: L.refetch }) : J.length > 0 ? /* @__PURE__ */ k("ul", {
160
+ className: "-mx-1 flex snap-x snap-mandatory gap-3 overflow-x-auto px-1 pb-1",
161
+ "aria-label": N("brainyrich.dashboard.sections.goals", "Goals on track"),
162
+ children: J.map((e) => /* @__PURE__ */ k("li", {
163
+ className: "w-40 shrink-0 snap-start",
164
+ children: /* @__PURE__ */ A("button", {
165
+ type: "button",
166
+ onClick: () => V(m(H, e.id)),
167
+ className: "flex h-full w-full flex-col items-start gap-2 rounded-2xl border border-border-subtle bg-bg-surface p-3 text-left motion-safe:transition-colors hover:bg-bg-elevated",
168
+ children: [
169
+ /* @__PURE__ */ k(r, {
170
+ value: _(e),
171
+ tone: v(e.status, e.projection),
172
+ size: 52,
173
+ strokeWidth: 6,
174
+ label: N("brainyrich.goals.card.funded", "funded")
175
+ }),
176
+ /* @__PURE__ */ k("p", {
177
+ className: "w-full truncate text-sm font-medium text-text-primary",
178
+ children: e.name
179
+ }),
180
+ /* @__PURE__ */ k("span", {
181
+ className: p("rounded-full px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wide", b(e)),
182
+ children: e.status === "ACHIEVED" ? M(e.status) : M(e.projection)
183
+ }),
184
+ /* @__PURE__ */ A("p", {
185
+ className: "text-xs text-text-secondary",
186
+ children: [
187
+ c(e.targetAmount, e.currency),
188
+ " ·",
189
+ " ",
190
+ l(e.targetDate)
191
+ ]
192
+ })
193
+ ]
194
+ })
195
+ }, e.id))
196
+ }) : /* @__PURE__ */ k(a, {
197
+ icon: /* @__PURE__ */ k(w, {
198
+ className: "size-6",
199
+ "aria-hidden": "true"
200
+ }),
201
+ title: N("brainyrich.dashboard.noGoals", "No goals yet"),
202
+ description: N("brainyrich.dashboard.noGoalsDesc", "Set a financial goal to start tracking progress.")
203
+ })]
204
+ }),
205
+ !R.isLoading && (R.isError || Y.length > 0) ? /* @__PURE__ */ A("section", {
206
+ "aria-labelledby": "br-mobile-portfolios",
207
+ className: "space-y-2",
208
+ children: [/* @__PURE__ */ A("div", {
209
+ className: "flex items-center justify-between",
210
+ children: [/* @__PURE__ */ k("h2", {
211
+ id: "br-mobile-portfolios",
212
+ className: "text-sm font-semibold text-text-primary",
213
+ children: N("brainyrich.dashboard.sections.portfolios", "Portfolios")
214
+ }), X(N("brainyrich.mobile.seeAll", "See all"), h(H, "brainyrich-portfolios"))]
215
+ }), R.isError ? /* @__PURE__ */ k(o, {
216
+ title: N("brainyrich.dashboard.portfoliosError", "Could not load portfolios"),
217
+ onRetry: R.refetch
218
+ }) : /* @__PURE__ */ k("div", {
219
+ className: "space-y-2",
220
+ children: Y.map((e) => /* @__PURE__ */ A(y, {
221
+ className: "flex items-center justify-between gap-3",
222
+ children: [/* @__PURE__ */ A("div", {
223
+ className: "min-w-0",
224
+ children: [/* @__PURE__ */ k("p", {
225
+ className: "truncate text-sm font-medium text-text-primary",
226
+ children: e.name
227
+ }), /* @__PURE__ */ k("p", {
228
+ className: "truncate text-xs text-text-secondary",
229
+ children: e.description ?? e.baseCurrency
230
+ })]
231
+ }), /* @__PURE__ */ k(j, {
232
+ variant: f(e.status),
233
+ className: "shrink-0",
234
+ children: e.status
235
+ })]
236
+ }, e.id))
237
+ })]
238
+ }) : null
239
+ ]
240
+ });
241
+ }
242
+ //#endregion
243
+ export { N as MobileDashboardView };
244
+
245
+ //# sourceMappingURL=MobileDashboardView.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MobileDashboardView.js","names":[],"sources":["../../../../src/brainyrich/pages/mobile/MobileDashboardView.tsx"],"sourcesContent":["import type { ReactNode } from 'react';\nimport { useNavigate } from 'react-router';\nimport { Badge } from '@burdenoff/fe-libs/ui';\nimport { ChevronRight, CircleCheck, Coins, Target, TrendingUp, Wallet } from 'lucide-react';\nimport type { BraGoal, BraInsight, BraNetWorthSummary, BraPortfolio } from '../../types/bra';\nimport {\n DonutChart,\n GlassCard,\n ProgressRing,\n useVizColors,\n type DonutDatum,\n} from '../../components/viz';\nimport { MobilePage } from '../../components/mobile';\nimport { useBrainyRich } from '../../providers/BrainyRichProvider';\nimport { goalDetailPath, sectionPath } from '../../navItems';\nimport { fundedPercent, goalTone } from '../../utils/goalMetrics';\nimport { cn } from '../../utils/cn';\nimport {\n EmptyState,\n ErrorState,\n LoadingState,\n formatCompact,\n formatDate,\n formatMoney,\n formatPct,\n statusBadgeVariant,\n} from '../_shared';\nimport {\n MobileCard,\n projectionChipClass,\n type MobileQueryState,\n type Translate,\n} from './mobileShared';\n\nexport interface MobileDashboardViewProps {\n translate: Translate;\n summary: MobileQueryState<BraNetWorthSummary | null | undefined>;\n /** Positive allocation slices, computed by the page so both layouts agree. */\n allocation: DonutDatum[];\n insights: MobileQueryState<BraInsight[]>;\n goals: MobileQueryState<BraGoal[]>;\n portfolios: MobileQueryState<BraPortfolio[]>;\n /** Renders one open insight with its acknowledge / resolve / dismiss actions. */\n renderInsight: (insight: BraInsight) => ReactNode;\n /** App-bar action that opens the new-account dialog. */\n newAccountAction: ReactNode;\n}\n\nfunction prettyLabel(value: string): string {\n const spaced = value.replace(/_/g, ' ').toLowerCase();\n return spaced.charAt(0).toUpperCase() + spaced.slice(1);\n}\n\n/**\n * Phone dashboard per `br-47-mobile-dashboard`: net-worth hero, condensed\n * invested / cash KPIs, allocation mini-chart, the attention list and a\n * horizontally scrolling strip of goal cards.\n */\nexport function MobileDashboardView({\n translate,\n summary,\n allocation,\n insights,\n goals,\n portfolios,\n renderInsight,\n newAccountAction,\n}: MobileDashboardViewProps) {\n const navigate = useNavigate();\n const { basePath = '/' } = useBrainyRich();\n const colors = useVizColors();\n const data = summary.data;\n const currency = data?.currency ?? 'USD';\n const allocationTotal = allocation.reduce((sum, row) => sum + row.value, 0);\n const topInsights = insights.data.slice(0, 3);\n const topGoals = goals.data.slice(0, 8);\n const topPortfolios = portfolios.data.slice(0, 3);\n\n const seeAll = (label: string, target: string) => (\n <button\n type=\"button\"\n onClick={() => navigate(target)}\n className=\"inline-flex min-h-11 items-center gap-0.5 text-xs font-medium text-text-secondary hover:text-text-primary\"\n >\n {label}\n <ChevronRight className=\"size-3.5\" aria-hidden=\"true\" />\n </button>\n );\n\n return (\n <MobilePage\n tourId=\"brainyrich-page-dashboard\"\n title={translate('brainyrich.dashboard.title', 'Wealth Dashboard')}\n subtitle={translate('brainyrich.mobile.dashboard.subtitle', 'Your wealth at a glance')}\n brandMark\n action={newAccountAction}\n >\n {/* Net worth hero + condensed KPIs */}\n {summary.isLoading ? (\n <LoadingState rows={2} />\n ) : summary.isError ? (\n <ErrorState\n title={translate('brainyrich.dashboard.netWorthError', 'Could not load net worth')}\n onRetry={summary.refetch}\n />\n ) : data ? (\n <>\n <GlassCard glow className=\"p-5\">\n <div className=\"flex items-start justify-between gap-2\">\n <p className=\"text-sm text-text-secondary\">\n {translate('brainyrich.dashboard.metrics.netWorth', 'Net Worth')}\n </p>\n <Wallet className=\"size-4 text-text-secondary\" aria-hidden=\"true\" />\n </div>\n <p\n className=\"mt-1 text-4xl font-semibold tracking-tight text-text-primary\"\n data-testid=\"brainyrich-mobile-networth\"\n >\n {formatCompact(data.totalNetWorth, currency)}\n </p>\n <p className=\"mt-1 text-xs text-text-secondary\">\n {translate(\n 'brainyrich.mobile.dashboard.accountsLinked',\n '{{count}} linked accounts',\n {\n count: data.accountCount,\n }\n )}\n </p>\n </GlassCard>\n <div className=\"grid grid-cols-2 gap-3\">\n <MobileCard>\n <div className=\"flex items-center gap-1.5 text-xs text-text-secondary\">\n <TrendingUp className=\"size-3.5\" aria-hidden=\"true\" />\n {translate('brainyrich.mobile.dashboard.invested', 'Invested')}\n </div>\n <p className=\"mt-1 text-xl font-semibold text-text-primary\">\n {formatCompact(data.totalMarketValue, currency)}\n </p>\n </MobileCard>\n <MobileCard>\n <div className=\"flex items-center gap-1.5 text-xs text-text-secondary\">\n <Coins className=\"size-3.5\" aria-hidden=\"true\" />\n {translate('brainyrich.mobile.dashboard.cash', 'Cash')}\n </div>\n <p className=\"mt-1 text-xl font-semibold text-text-primary\">\n {formatCompact(data.totalCash, currency)}\n </p>\n </MobileCard>\n </div>\n </>\n ) : (\n <EmptyState\n icon={<Wallet className=\"size-6\" aria-hidden=\"true\" />}\n title={translate('brainyrich.dashboard.noSummary', 'No financial data yet')}\n description={translate(\n 'brainyrich.dashboard.noSummaryDesc',\n 'Link your first account to see your net worth here.'\n )}\n />\n )}\n\n {/* Allocation mini-chart */}\n {!summary.isLoading && !summary.isError && allocation.length > 0 ? (\n <MobileCard>\n <h2 className=\"text-sm font-semibold text-text-primary\">\n {translate('brainyrich.dashboard.sections.positiveAssetAllocation', 'Asset allocation')}\n </h2>\n <div className=\"mt-3 flex items-center gap-4\">\n <div className=\"w-28 shrink-0\">\n <DonutChart\n data={allocation}\n legend={false}\n height={112}\n valueFormatter={(v) => formatMoney(v, currency)}\n />\n </div>\n <ul\n className=\"min-w-0 flex-1 space-y-1.5\"\n aria-label={translate('brainyrich.dashboard.allocationLegend', 'Allocation legend')}\n >\n {allocation.map((row, i) => (\n <li key={row.label} className=\"flex items-center gap-2 text-xs\">\n <span\n aria-hidden=\"true\"\n className=\"size-2 shrink-0 rounded-full\"\n style={{\n background: row.tone\n ? colors.tone(row.tone)\n : colors.palette[i % colors.palette.length],\n }}\n />\n <span className=\"truncate text-text-secondary\">{row.label}</span>\n <span className=\"ml-auto font-medium tabular-nums text-text-primary\">\n {formatPct(allocationTotal > 0 ? (row.value / allocationTotal) * 100 : 0, 0)}\n </span>\n </li>\n ))}\n </ul>\n </div>\n </MobileCard>\n ) : null}\n\n {/* Needs attention */}\n <section aria-labelledby=\"br-mobile-attention\" className=\"space-y-2\">\n <h2 id=\"br-mobile-attention\" className=\"text-sm font-semibold text-text-primary\">\n {translate('brainyrich.dashboard.sections.needsAttention', 'Needs attention')}\n </h2>\n {insights.isLoading ? (\n <LoadingState rows={2} />\n ) : insights.isError ? (\n <ErrorState onRetry={insights.refetch} />\n ) : topInsights.length > 0 ? (\n <div className=\"space-y-2\">\n {topInsights.map((insight) => (\n <div key={insight.id}>{renderInsight(insight)}</div>\n ))}\n </div>\n ) : (\n <MobileCard className=\"flex items-center gap-3\">\n <CircleCheck className=\"size-5 text-status-success-text\" aria-hidden=\"true\" />\n <p className=\"text-sm text-text-secondary\">\n {translate(\n 'brainyrich.dashboard.noInsightsDesc',\n 'There are no open insights that need your attention.'\n )}\n </p>\n </MobileCard>\n )}\n </section>\n\n {/* Goals strip */}\n <section aria-labelledby=\"br-mobile-goals\" className=\"space-y-2\">\n <div className=\"flex items-center justify-between\">\n <h2 id=\"br-mobile-goals\" className=\"text-sm font-semibold text-text-primary\">\n {translate('brainyrich.dashboard.sections.goals', 'Goals on track')}\n </h2>\n {topGoals.length > 0\n ? seeAll(\n translate('brainyrich.mobile.seeAll', 'See all'),\n sectionPath(basePath, 'brainyrich-goals')\n )\n : null}\n </div>\n {goals.isLoading ? (\n <LoadingState rows={1} />\n ) : goals.isError ? (\n <ErrorState onRetry={goals.refetch} />\n ) : topGoals.length > 0 ? (\n <ul\n className=\"-mx-1 flex snap-x snap-mandatory gap-3 overflow-x-auto px-1 pb-1\"\n aria-label={translate('brainyrich.dashboard.sections.goals', 'Goals on track')}\n >\n {topGoals.map((goal) => {\n const pct = fundedPercent(goal);\n return (\n <li key={goal.id} className=\"w-40 shrink-0 snap-start\">\n <button\n type=\"button\"\n onClick={() => navigate(goalDetailPath(basePath, goal.id))}\n className=\"flex h-full w-full flex-col items-start gap-2 rounded-2xl border border-border-subtle bg-bg-surface p-3 text-left motion-safe:transition-colors hover:bg-bg-elevated\"\n >\n <ProgressRing\n value={pct}\n tone={goalTone(goal.status, goal.projection)}\n size={52}\n strokeWidth={6}\n label={translate('brainyrich.goals.card.funded', 'funded')}\n />\n <p className=\"w-full truncate text-sm font-medium text-text-primary\">\n {goal.name}\n </p>\n <span\n className={cn(\n 'rounded-full px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wide',\n projectionChipClass(goal)\n )}\n >\n {goal.status === 'ACHIEVED'\n ? prettyLabel(goal.status)\n : prettyLabel(goal.projection)}\n </span>\n <p className=\"text-xs text-text-secondary\">\n {formatCompact(goal.targetAmount, goal.currency)} ·{' '}\n {formatDate(goal.targetDate)}\n </p>\n </button>\n </li>\n );\n })}\n </ul>\n ) : (\n <EmptyState\n icon={<Target className=\"size-6\" aria-hidden=\"true\" />}\n title={translate('brainyrich.dashboard.noGoals', 'No goals yet')}\n description={translate(\n 'brainyrich.dashboard.noGoalsDesc',\n 'Set a financial goal to start tracking progress.'\n )}\n />\n )}\n </section>\n\n {/* Portfolios: hidden while loading or when there are none, but a failed\n load stays visible with a retry so the phone never silently drops it. */}\n {!portfolios.isLoading && (portfolios.isError || topPortfolios.length > 0) ? (\n <section aria-labelledby=\"br-mobile-portfolios\" className=\"space-y-2\">\n <div className=\"flex items-center justify-between\">\n <h2 id=\"br-mobile-portfolios\" className=\"text-sm font-semibold text-text-primary\">\n {translate('brainyrich.dashboard.sections.portfolios', 'Portfolios')}\n </h2>\n {seeAll(\n translate('brainyrich.mobile.seeAll', 'See all'),\n sectionPath(basePath, 'brainyrich-portfolios')\n )}\n </div>\n {portfolios.isError ? (\n <ErrorState\n title={translate('brainyrich.dashboard.portfoliosError', 'Could not load portfolios')}\n onRetry={portfolios.refetch}\n />\n ) : (\n <div className=\"space-y-2\">\n {topPortfolios.map((portfolio) => (\n <MobileCard key={portfolio.id} className=\"flex items-center justify-between gap-3\">\n <div className=\"min-w-0\">\n <p className=\"truncate text-sm font-medium text-text-primary\">\n {portfolio.name}\n </p>\n <p className=\"truncate text-xs text-text-secondary\">\n {portfolio.description ?? portfolio.baseCurrency}\n </p>\n </div>\n <Badge variant={statusBadgeVariant(portfolio.status)} className=\"shrink-0\">\n {portfolio.status}\n </Badge>\n </MobileCard>\n ))}\n </div>\n )}\n </section>\n ) : null}\n </MobilePage>\n );\n}\n"],"mappings":"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"}
@@ -0,0 +1,30 @@
1
+ import { BraGoal } from '../../types/bra';
2
+ import { Translate } from './mobileShared';
3
+ export interface MobileGoalsViewProps {
4
+ translate: Translate;
5
+ goals: BraGoal[];
6
+ isLoading: boolean;
7
+ isError: boolean;
8
+ refetch: () => void;
9
+ hasMore: boolean;
10
+ loadMore: () => void;
11
+ isFetchingNextPage: boolean;
12
+ totals: {
13
+ totalTarget: number;
14
+ totalMonthly: number;
15
+ atRisk: number;
16
+ };
17
+ currency: string;
18
+ onCreate: () => void;
19
+ onOpen: (goal: BraGoal) => void;
20
+ onEdit: (goal: BraGoal) => void;
21
+ onArchive: (goal: BraGoal) => void;
22
+ }
23
+ /**
24
+ * Phone goals list per `br-49-mobile-goals`: a simplified funding summary, one
25
+ * card per goal with a progress ring and projection badge, and a floating
26
+ * "New goal" button. Tapping a card opens the same detail and funding sheet
27
+ * as desktop.
28
+ */
29
+ export declare function MobileGoalsView({ translate, goals, isLoading, isError, refetch, hasMore, loadMore, isFetchingNextPage, totals, currency, onCreate, onOpen, onEdit, onArchive, }: MobileGoalsViewProps): import("react/jsx-runtime").JSX.Element;
30
+ //# sourceMappingURL=MobileGoalsView.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MobileGoalsView.d.ts","sourceRoot":"","sources":["../../../../src/brainyrich/pages/mobile/MobileGoalsView.tsx"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAc/C,OAAO,EAAmC,KAAK,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAEjF,MAAM,WAAW,oBAAoB;IACnC,SAAS,EAAE,SAAS,CAAC;IACrB,KAAK,EAAE,OAAO,EAAE,CAAC;IACjB,SAAS,EAAE,OAAO,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,kBAAkB,EAAE,OAAO,CAAC;IAC5B,MAAM,EAAE;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IACtE,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,MAAM,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IAChC,MAAM,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IAChC,SAAS,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;CACpC;AAUD;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,EAC9B,SAAS,EACT,KAAK,EACL,SAAS,EACT,OAAO,EACP,OAAO,EACP,OAAO,EACP,QAAQ,EACR,kBAAkB,EAClB,MAAM,EACN,QAAQ,EACR,QAAQ,EACR,MAAM,EACN,MAAM,EACN,SAAS,GACV,EAAE,oBAAoB,2CAwKtB"}
@@ -0,0 +1,180 @@
1
+ import { LoadMore as e } from "../../components/LoadMore.js";
2
+ import { ProgressRing as t } from "../../components/viz.js";
3
+ import "../../components/index.js";
4
+ import { EmptyState as n, ErrorState as r, LoadingState as i, formatCompact as a, formatDate as o, formatMoney as s, formatPct as c } from "../_shared.js";
5
+ import { cn as l } from "../../utils/cn.js";
6
+ import { MobileFab as u, MobilePage as d } from "../../components/mobile/MobilePage.js";
7
+ import "../../components/mobile/index.js";
8
+ import { fundedPercent as f, goalTone as p } from "../../utils/goalMetrics.js";
9
+ import { MobileCard as m, projectionChipClass as h } from "./mobileShared.js";
10
+ import { Link2 as g, MoreHorizontal as _, Plus as v, RefreshCw as y, Target as b, Trash2 as x } from "lucide-react";
11
+ import { Fragment as S, jsx as C, jsxs as w } from "react/jsx-runtime";
12
+ import { Button as T, DropdownMenu as E, DropdownMenuContent as D, DropdownMenuItem as O, DropdownMenuSeparator as k, DropdownMenuTrigger as A } from "@burdenoff/fe-libs/ui";
13
+ //#region src/brainyrich/pages/mobile/MobileGoalsView.tsx
14
+ function j(e) {
15
+ return e.toLowerCase().split("_").map((e) => e.charAt(0).toUpperCase() + e.slice(1)).join(" ");
16
+ }
17
+ function M({ translate: M, goals: N, isLoading: P, isError: F, refetch: I, hasMore: L, loadMore: R, isFetchingNextPage: z, totals: B, currency: V, onCreate: H, onOpen: U, onEdit: W, onArchive: G }) {
18
+ let K = M("brainyrich.goals.actions.createGoal", "New goal");
19
+ return /* @__PURE__ */ C(d, {
20
+ tourId: "brainyrich-page-goals",
21
+ title: M("brainyrich.mobile.goals.title", "Goals"),
22
+ subtitle: M("brainyrich.mobile.goals.count", "{{count}} goals", { count: N.length }),
23
+ fab: /* @__PURE__ */ C(u, {
24
+ label: K,
25
+ icon: /* @__PURE__ */ C(v, {
26
+ className: "size-4",
27
+ "aria-hidden": "true"
28
+ }),
29
+ onClick: H
30
+ }),
31
+ children: P ? /* @__PURE__ */ C(i, { rows: 4 }) : F ? /* @__PURE__ */ C(r, {
32
+ title: M("brainyrich.goals.error.title", "Could not load goals"),
33
+ description: M("brainyrich.goals.error.description", "We could not reach the goals service. Please try again."),
34
+ onRetry: I
35
+ }) : N.length === 0 ? /* @__PURE__ */ C(n, {
36
+ icon: /* @__PURE__ */ C(b, {
37
+ className: "size-6",
38
+ "aria-hidden": "true"
39
+ }),
40
+ title: M("brainyrich.goals.empty.title", "No financial goals yet"),
41
+ description: M("brainyrich.goals.empty.description", "Create your first goal to start tracking progress toward a milestone.")
42
+ }) : /* @__PURE__ */ w(S, { children: [
43
+ /* @__PURE__ */ w(m, {
44
+ className: "grid grid-cols-3 divide-x divide-border-subtle p-0",
45
+ children: [
46
+ /* @__PURE__ */ w("div", {
47
+ className: "p-3",
48
+ children: [/* @__PURE__ */ C("p", {
49
+ className: "text-[11px] text-text-secondary",
50
+ children: M("brainyrich.goals.metrics.totalTarget", "Total target")
51
+ }), /* @__PURE__ */ C("p", {
52
+ className: "mt-0.5 truncate text-base font-semibold text-text-primary",
53
+ children: a(B.totalTarget, V)
54
+ })]
55
+ }),
56
+ /* @__PURE__ */ w("div", {
57
+ className: "p-3",
58
+ children: [/* @__PURE__ */ C("p", {
59
+ className: "text-[11px] text-text-secondary",
60
+ children: M("brainyrich.goals.card.monthly", "Monthly")
61
+ }), /* @__PURE__ */ C("p", {
62
+ className: "mt-0.5 truncate text-base font-semibold text-text-primary",
63
+ children: a(B.totalMonthly, V)
64
+ })]
65
+ }),
66
+ /* @__PURE__ */ w("div", {
67
+ className: "p-3",
68
+ children: [/* @__PURE__ */ C("p", {
69
+ className: "text-[11px] text-text-secondary",
70
+ children: M("brainyrich.goals.metrics.atRisk", "Needs attention")
71
+ }), /* @__PURE__ */ C("p", {
72
+ className: l("mt-0.5 text-base font-semibold", B.atRisk > 0 ? "text-status-error-text" : "text-text-primary"),
73
+ children: B.atRisk
74
+ })]
75
+ })
76
+ ]
77
+ }),
78
+ /* @__PURE__ */ C("ul", {
79
+ className: "space-y-3",
80
+ "aria-label": M("brainyrich.goals.title", "Financial Goals"),
81
+ children: N.map((e) => {
82
+ let n = f(e), r = e.status === "ACHIEVED";
83
+ return /* @__PURE__ */ C("li", { children: /* @__PURE__ */ w(m, {
84
+ className: "flex items-center gap-3 p-3",
85
+ children: [/* @__PURE__ */ w("button", {
86
+ type: "button",
87
+ onClick: () => U(e),
88
+ className: "flex min-h-16 min-w-0 flex-1 items-center gap-3 text-left",
89
+ "aria-label": M("brainyrich.mobile.goals.open", "Open {{name}}", { name: e.name }),
90
+ children: [/* @__PURE__ */ C(t, {
91
+ value: n,
92
+ tone: p(e.status, e.projection),
93
+ size: 64,
94
+ strokeWidth: 7,
95
+ label: M("brainyrich.goals.card.funded", "funded")
96
+ }), /* @__PURE__ */ w("span", {
97
+ className: "min-w-0 flex-1",
98
+ children: [
99
+ /* @__PURE__ */ C("span", {
100
+ className: "block truncate text-base font-medium text-text-primary",
101
+ children: e.name
102
+ }),
103
+ /* @__PURE__ */ w("span", {
104
+ className: "mt-1 flex flex-wrap items-center gap-2",
105
+ children: [/* @__PURE__ */ C("span", {
106
+ className: "text-sm tabular-nums text-text-secondary",
107
+ children: c(n, 0)
108
+ }), /* @__PURE__ */ C("span", {
109
+ className: l("rounded-md px-1.5 py-0.5 text-[10px] font-semibold uppercase tracking-wide", h(e)),
110
+ children: j(r ? e.status : e.projection)
111
+ })]
112
+ }),
113
+ /* @__PURE__ */ w("span", {
114
+ className: "mt-1 block truncate text-xs text-text-secondary",
115
+ children: [
116
+ s(e.targetAmount, e.currency),
117
+ " ·",
118
+ " ",
119
+ o(e.targetDate)
120
+ ]
121
+ })
122
+ ]
123
+ })]
124
+ }), /* @__PURE__ */ w(E, { children: [/* @__PURE__ */ C(A, {
125
+ asChild: !0,
126
+ children: /* @__PURE__ */ C(T, {
127
+ variant: "ghost",
128
+ size: "icon",
129
+ className: "size-11 shrink-0",
130
+ "aria-label": M("brainyrich.goals.actions.menu", "Goal actions"),
131
+ children: /* @__PURE__ */ C(_, {
132
+ className: "size-4",
133
+ "aria-hidden": "true"
134
+ })
135
+ })
136
+ }), /* @__PURE__ */ w(D, {
137
+ align: "end",
138
+ children: [
139
+ /* @__PURE__ */ w(O, {
140
+ onSelect: () => U(e),
141
+ children: [/* @__PURE__ */ C(g, {
142
+ className: "size-4",
143
+ "aria-hidden": "true"
144
+ }), M("brainyrich.goals.actions.manageFunding", "Manage funding")]
145
+ }),
146
+ /* @__PURE__ */ w(O, {
147
+ onSelect: () => W(e),
148
+ children: [/* @__PURE__ */ C(y, {
149
+ className: "size-4",
150
+ "aria-hidden": "true"
151
+ }), M("brainyrich.goals.actions.editGoal", "Edit goal")]
152
+ }),
153
+ /* @__PURE__ */ C(k, {}),
154
+ /* @__PURE__ */ w(O, {
155
+ className: "text-status-error-text focus:text-status-error-text",
156
+ onSelect: () => G(e),
157
+ children: [/* @__PURE__ */ C(x, {
158
+ className: "size-4",
159
+ "aria-hidden": "true"
160
+ }), M("brainyrich.goals.actions.archive", "Archive goal")]
161
+ })
162
+ ]
163
+ })] })]
164
+ }) }, e.id);
165
+ })
166
+ }),
167
+ /* @__PURE__ */ C(e, {
168
+ hasMore: L,
169
+ loading: z,
170
+ onClick: R,
171
+ label: M("brainyrich.goals.actions.loadMore", "Load more goals"),
172
+ loadingLabel: M("brainyrich.goals.actions.loadingMore", "Loading…")
173
+ })
174
+ ] })
175
+ });
176
+ }
177
+ //#endregion
178
+ export { M as MobileGoalsView };
179
+
180
+ //# sourceMappingURL=MobileGoalsView.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MobileGoalsView.js","names":[],"sources":["../../../../src/brainyrich/pages/mobile/MobileGoalsView.tsx"],"sourcesContent":["import {\n Button,\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuSeparator,\n DropdownMenuTrigger,\n} from '@burdenoff/fe-libs/ui';\nimport { Link2, MoreHorizontal, Plus, RefreshCw, Target, Trash2 } from 'lucide-react';\nimport type { BraGoal } from '../../types/bra';\nimport { LoadMore, ProgressRing } from '../../components';\nimport { MobileFab, MobilePage } from '../../components/mobile';\nimport { fundedPercent, goalTone } from '../../utils/goalMetrics';\nimport { cn } from '../../utils/cn';\nimport {\n EmptyState,\n ErrorState,\n LoadingState,\n formatCompact,\n formatDate,\n formatMoney,\n formatPct,\n} from '../_shared';\nimport { MobileCard, projectionChipClass, type Translate } from './mobileShared';\n\nexport interface MobileGoalsViewProps {\n translate: Translate;\n goals: BraGoal[];\n isLoading: boolean;\n isError: boolean;\n refetch: () => void;\n hasMore: boolean;\n loadMore: () => void;\n isFetchingNextPage: boolean;\n totals: { totalTarget: number; totalMonthly: number; atRisk: number };\n currency: string;\n onCreate: () => void;\n onOpen: (goal: BraGoal) => void;\n onEdit: (goal: BraGoal) => void;\n onArchive: (goal: BraGoal) => void;\n}\n\nfunction humanize(value: string): string {\n return value\n .toLowerCase()\n .split('_')\n .map((part) => part.charAt(0).toUpperCase() + part.slice(1))\n .join(' ');\n}\n\n/**\n * Phone goals list per `br-49-mobile-goals`: a simplified funding summary, one\n * card per goal with a progress ring and projection badge, and a floating\n * \"New goal\" button. Tapping a card opens the same detail and funding sheet\n * as desktop.\n */\nexport function MobileGoalsView({\n translate,\n goals,\n isLoading,\n isError,\n refetch,\n hasMore,\n loadMore,\n isFetchingNextPage,\n totals,\n currency,\n onCreate,\n onOpen,\n onEdit,\n onArchive,\n}: MobileGoalsViewProps) {\n const newGoalLabel = translate('brainyrich.goals.actions.createGoal', 'New goal');\n\n return (\n <MobilePage\n tourId=\"brainyrich-page-goals\"\n title={translate('brainyrich.mobile.goals.title', 'Goals')}\n subtitle={translate('brainyrich.mobile.goals.count', '{{count}} goals', {\n count: goals.length,\n })}\n fab={\n <MobileFab\n label={newGoalLabel}\n icon={<Plus className=\"size-4\" aria-hidden=\"true\" />}\n onClick={onCreate}\n />\n }\n >\n {isLoading ? (\n <LoadingState rows={4} />\n ) : isError ? (\n <ErrorState\n title={translate('brainyrich.goals.error.title', 'Could not load goals')}\n description={translate(\n 'brainyrich.goals.error.description',\n 'We could not reach the goals service. Please try again.'\n )}\n onRetry={refetch}\n />\n ) : goals.length === 0 ? (\n <EmptyState\n icon={<Target className=\"size-6\" aria-hidden=\"true\" />}\n title={translate('brainyrich.goals.empty.title', 'No financial goals yet')}\n description={translate(\n 'brainyrich.goals.empty.description',\n 'Create your first goal to start tracking progress toward a milestone.'\n )}\n />\n ) : (\n <>\n {/* Simplified funding summary */}\n <MobileCard className=\"grid grid-cols-3 divide-x divide-border-subtle p-0\">\n <div className=\"p-3\">\n <p className=\"text-[11px] text-text-secondary\">\n {translate('brainyrich.goals.metrics.totalTarget', 'Total target')}\n </p>\n <p className=\"mt-0.5 truncate text-base font-semibold text-text-primary\">\n {formatCompact(totals.totalTarget, currency)}\n </p>\n </div>\n <div className=\"p-3\">\n <p className=\"text-[11px] text-text-secondary\">\n {translate('brainyrich.goals.card.monthly', 'Monthly')}\n </p>\n <p className=\"mt-0.5 truncate text-base font-semibold text-text-primary\">\n {formatCompact(totals.totalMonthly, currency)}\n </p>\n </div>\n <div className=\"p-3\">\n <p className=\"text-[11px] text-text-secondary\">\n {translate('brainyrich.goals.metrics.atRisk', 'Needs attention')}\n </p>\n <p\n className={cn(\n 'mt-0.5 text-base font-semibold',\n totals.atRisk > 0 ? 'text-status-error-text' : 'text-text-primary'\n )}\n >\n {totals.atRisk}\n </p>\n </div>\n </MobileCard>\n\n <ul\n className=\"space-y-3\"\n aria-label={translate('brainyrich.goals.title', 'Financial Goals')}\n >\n {goals.map((goal) => {\n const pct = fundedPercent(goal);\n const achieved = goal.status === 'ACHIEVED';\n return (\n <li key={goal.id}>\n <MobileCard className=\"flex items-center gap-3 p-3\">\n <button\n type=\"button\"\n onClick={() => onOpen(goal)}\n className=\"flex min-h-16 min-w-0 flex-1 items-center gap-3 text-left\"\n aria-label={translate('brainyrich.mobile.goals.open', 'Open {{name}}', {\n name: goal.name,\n })}\n >\n <ProgressRing\n value={pct}\n tone={goalTone(goal.status, goal.projection)}\n size={64}\n strokeWidth={7}\n label={translate('brainyrich.goals.card.funded', 'funded')}\n />\n <span className=\"min-w-0 flex-1\">\n <span className=\"block truncate text-base font-medium text-text-primary\">\n {goal.name}\n </span>\n <span className=\"mt-1 flex flex-wrap items-center gap-2\">\n <span className=\"text-sm tabular-nums text-text-secondary\">\n {formatPct(pct, 0)}\n </span>\n <span\n className={cn(\n 'rounded-md px-1.5 py-0.5 text-[10px] font-semibold uppercase tracking-wide',\n projectionChipClass(goal)\n )}\n >\n {achieved ? humanize(goal.status) : humanize(goal.projection)}\n </span>\n </span>\n <span className=\"mt-1 block truncate text-xs text-text-secondary\">\n {formatMoney(goal.targetAmount, goal.currency)} ·{' '}\n {formatDate(goal.targetDate)}\n </span>\n </span>\n </button>\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button\n variant=\"ghost\"\n size=\"icon\"\n className=\"size-11 shrink-0\"\n aria-label={translate('brainyrich.goals.actions.menu', 'Goal actions')}\n >\n <MoreHorizontal className=\"size-4\" aria-hidden=\"true\" />\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\">\n <DropdownMenuItem onSelect={() => onOpen(goal)}>\n <Link2 className=\"size-4\" aria-hidden=\"true\" />\n {translate('brainyrich.goals.actions.manageFunding', 'Manage funding')}\n </DropdownMenuItem>\n <DropdownMenuItem onSelect={() => onEdit(goal)}>\n <RefreshCw className=\"size-4\" aria-hidden=\"true\" />\n {translate('brainyrich.goals.actions.editGoal', 'Edit goal')}\n </DropdownMenuItem>\n <DropdownMenuSeparator />\n <DropdownMenuItem\n className=\"text-status-error-text focus:text-status-error-text\"\n onSelect={() => onArchive(goal)}\n >\n <Trash2 className=\"size-4\" aria-hidden=\"true\" />\n {translate('brainyrich.goals.actions.archive', 'Archive goal')}\n </DropdownMenuItem>\n </DropdownMenuContent>\n </DropdownMenu>\n </MobileCard>\n </li>\n );\n })}\n </ul>\n\n <LoadMore\n hasMore={hasMore}\n loading={isFetchingNextPage}\n onClick={loadMore}\n label={translate('brainyrich.goals.actions.loadMore', 'Load more goals')}\n loadingLabel={translate('brainyrich.goals.actions.loadingMore', 'Loading…')}\n />\n </>\n )}\n </MobilePage>\n );\n}\n"],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ import { ReactNode } from 'react';
2
+ import { BraAccountType, BraGoal } from '../../types/bra';
3
+ export type Translate = (key: string, fallback: string, params?: Record<string, string | number>) => string;
4
+ /** The slice of a query result the mobile views render from. */
5
+ export interface MobileQueryState<T> {
6
+ data: T;
7
+ isLoading: boolean;
8
+ isError: boolean;
9
+ refetch: () => void;
10
+ }
11
+ /** Full-width, touch-friendly card surface used across the phone layouts. */
12
+ export declare function MobileCard({ children, className }: {
13
+ children: ReactNode;
14
+ className?: string;
15
+ }): import("react/jsx-runtime").JSX.Element;
16
+ /**
17
+ * Status-chip colours for a goal's projection. An achieved goal reads as a
18
+ * success regardless of its last projection. Colour is never the only signal:
19
+ * every chip also carries the projection label.
20
+ */
21
+ export declare function projectionChipClass(goal: Pick<BraGoal, 'status' | 'projection'>): string;
22
+ /** Account-type chip colours: liabilities read as errors, everything else neutral-brand. */
23
+ export declare function accountTypeChipClass(type: BraAccountType): string;
24
+ //# sourceMappingURL=mobileShared.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mobileShared.d.ts","sourceRoot":"","sources":["../../../../src/brainyrich/pages/mobile/mobileShared.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AACvC,OAAO,KAAK,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAG/D,MAAM,MAAM,SAAS,GAAG,CACtB,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,MAAM,EAChB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,KACrC,MAAM,CAAC;AAEZ,gEAAgE;AAChE,MAAM,WAAW,gBAAgB,CAAC,CAAC;IACjC,IAAI,EAAE,CAAC,CAAC;IACR,SAAS,EAAE,OAAO,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAED,6EAA6E;AAC7E,wBAAgB,UAAU,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAE,EAAE;IAAE,QAAQ,EAAE,SAAS,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,2CAM9F;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,IAAI,CAAC,OAAO,EAAE,QAAQ,GAAG,YAAY,CAAC,GAAG,MAAM,CAYxF;AAED,4FAA4F;AAC5F,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,cAAc,GAAG,MAAM,CAIjE"}
@@ -0,0 +1,25 @@
1
+ import { cn as e } from "../../utils/cn.js";
2
+ import { jsx as t } from "react/jsx-runtime";
3
+ //#region src/brainyrich/pages/mobile/mobileShared.tsx
4
+ function n({ children: n, className: r }) {
5
+ return /* @__PURE__ */ t("div", {
6
+ className: e("rounded-2xl border border-border-subtle bg-bg-surface p-4", r),
7
+ children: n
8
+ });
9
+ }
10
+ function r(e) {
11
+ if (e.status === "ACHIEVED") return "bg-status-success-bg text-status-success-text";
12
+ switch (e.projection) {
13
+ case "AHEAD":
14
+ case "ON_TRACK": return "bg-status-success-bg text-status-success-text";
15
+ case "BEHIND": return "bg-status-warning-bg text-status-warning-text";
16
+ default: return "bg-status-error-bg text-status-error-text";
17
+ }
18
+ }
19
+ function i(e) {
20
+ return e === "LIABILITY" ? "bg-status-error-bg text-status-error-text" : e === "CRYPTO" ? "bg-status-warning-bg text-status-warning-text" : "bg-status-success-bg text-status-success-text";
21
+ }
22
+ //#endregion
23
+ export { n as MobileCard, i as accountTypeChipClass, r as projectionChipClass };
24
+
25
+ //# sourceMappingURL=mobileShared.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mobileShared.js","names":[],"sources":["../../../../src/brainyrich/pages/mobile/mobileShared.tsx"],"sourcesContent":["import type { ReactNode } from 'react';\nimport type { BraAccountType, BraGoal } from '../../types/bra';\nimport { cn } from '../../utils/cn';\n\nexport type Translate = (\n key: string,\n fallback: string,\n params?: Record<string, string | number>\n) => string;\n\n/** The slice of a query result the mobile views render from. */\nexport interface MobileQueryState<T> {\n data: T;\n isLoading: boolean;\n isError: boolean;\n refetch: () => void;\n}\n\n/** Full-width, touch-friendly card surface used across the phone layouts. */\nexport function MobileCard({ children, className }: { children: ReactNode; className?: string }) {\n return (\n <div className={cn('rounded-2xl border border-border-subtle bg-bg-surface p-4', className)}>\n {children}\n </div>\n );\n}\n\n/**\n * Status-chip colours for a goal's projection. An achieved goal reads as a\n * success regardless of its last projection. Colour is never the only signal:\n * every chip also carries the projection label.\n */\nexport function projectionChipClass(goal: Pick<BraGoal, 'status' | 'projection'>): string {\n if (goal.status === 'ACHIEVED') return 'bg-status-success-bg text-status-success-text';\n switch (goal.projection) {\n case 'AHEAD':\n case 'ON_TRACK':\n return 'bg-status-success-bg text-status-success-text';\n case 'BEHIND':\n return 'bg-status-warning-bg text-status-warning-text';\n case 'AT_RISK':\n default:\n return 'bg-status-error-bg text-status-error-text';\n }\n}\n\n/** Account-type chip colours: liabilities read as errors, everything else neutral-brand. */\nexport function accountTypeChipClass(type: BraAccountType): string {\n if (type === 'LIABILITY') return 'bg-status-error-bg text-status-error-text';\n if (type === 'CRYPTO') return 'bg-status-warning-bg text-status-warning-text';\n return 'bg-status-success-bg text-status-success-text';\n}\n"],"mappings":";;;AAmBA,SAAgB,EAAW,EAAE,aAAU,gBAA0D;CAC/F,OACE,kBAAC,OAAD;EAAK,WAAW,EAAG,6DAA6D,CAAS;EACtF;CACE,CAAA;AAET;AAOA,SAAgB,EAAoB,GAAsD;CACxF,IAAI,EAAK,WAAW,YAAY,OAAO;CACvC,QAAQ,EAAK,YAAb;EACE,KAAK;EACL,KAAK,YACH,OAAO;EACT,KAAK,UACH,OAAO;EAET,SACE,OAAO;CACX;AACF;AAGA,SAAgB,EAAqB,GAA8B;CAGjE,OAFI,MAAS,cAAoB,8CAC7B,MAAS,WAAiB,kDACvB;AACT"}
@@ -0,0 +1,10 @@
1
+ import e from "../../node_modules/clsx/dist/clsx.js";
2
+ import { twMerge as t } from "../../node_modules/tailwind-merge/dist/bundle-mjs.js";
3
+ //#region src/brainyrich/utils/cn.ts
4
+ function n(...n) {
5
+ return t(e(n));
6
+ }
7
+ //#endregion
8
+ export { n as cn };
9
+
10
+ //# sourceMappingURL=cn.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cn.js","names":[],"sources":["../../../src/brainyrich/utils/cn.ts"],"sourcesContent":["import { type ClassValue, clsx } from 'clsx';\nimport { twMerge } from 'tailwind-merge';\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n"],"mappings":";;;AAGA,SAAgB,EAAG,GAAG,GAAsB;CAC1C,OAAO,EAAQ,EAAK,CAAM,CAAC;AAC7B"}
@@ -0,0 +1,13 @@
1
+ import { BraGoal, BraGoalProjection, BraGoalStatus } from '../types/bra';
2
+ import { VizTone } from '../components/viz';
3
+ /** Sum of the fixed-amount funding links wired to a goal (real backend value). */
4
+ export declare function goalFixedFunded(goal: Pick<BraGoal, 'fundings'>): number;
5
+ /**
6
+ * Funded-progress percentage for a goal. ACHIEVED goals read 100%; otherwise the
7
+ * sum of fixed funding links over the target — every input is wired backend data,
8
+ * never fabricated.
9
+ */
10
+ export declare function fundedPercent(goal: Pick<BraGoal, 'fundings' | 'targetAmount' | 'status'>): number;
11
+ /** Ring/accent tone derived from the goal's real status + projection. */
12
+ export declare function goalTone(status: BraGoalStatus, projection: BraGoalProjection): VizTone;
13
+ //# sourceMappingURL=goalMetrics.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"goalMetrics.d.ts","sourceRoot":"","sources":["../../../src/brainyrich/utils/goalMetrics.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC9E,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAEjD,kFAAkF;AAClF,wBAAgB,eAAe,CAAC,IAAI,EAAE,IAAI,CAAC,OAAO,EAAE,UAAU,CAAC,GAAG,MAAM,CAIvE;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,IAAI,CAAC,OAAO,EAAE,UAAU,GAAG,cAAc,GAAG,QAAQ,CAAC,GAAG,MAAM,CAIjG;AAED,yEAAyE;AACzE,wBAAgB,QAAQ,CAAC,MAAM,EAAE,aAAa,EAAE,UAAU,EAAE,iBAAiB,GAAG,OAAO,CAYtF"}