@burdenoff/microfe-brainyrich 2026.828.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 (39) hide show
  1. package/dist/brainyrich/BrainyRichRoot.d.ts.map +1 -1
  2. package/dist/brainyrich/BrainyRichRoot.js +59 -59
  3. package/dist/brainyrich/BrainyRichRoot.js.map +1 -1
  4. package/dist/brainyrich/components/viz.d.ts.map +1 -1
  5. package/dist/brainyrich/components/viz.js +149 -148
  6. package/dist/brainyrich/components/viz.js.map +1 -1
  7. package/dist/brainyrich/hooks/useTr.d.ts +17 -0
  8. package/dist/brainyrich/hooks/useTr.d.ts.map +1 -0
  9. package/dist/brainyrich/hooks/useTr.js +14 -0
  10. package/dist/brainyrich/hooks/useTr.js.map +1 -0
  11. package/dist/brainyrich/pages/AiAssistantPage.js +192 -192
  12. package/dist/brainyrich/pages/AiAssistantPage.js.map +1 -1
  13. package/dist/brainyrich/pages/AnalyticsPage.d.ts.map +1 -1
  14. package/dist/brainyrich/pages/AnalyticsPage.js +253 -252
  15. package/dist/brainyrich/pages/AnalyticsPage.js.map +1 -1
  16. package/dist/brainyrich/pages/DashboardPage.js +115 -115
  17. package/dist/brainyrich/pages/DashboardPage.js.map +1 -1
  18. package/dist/brainyrich/pages/FamilyPage.js +59 -59
  19. package/dist/brainyrich/pages/FamilyPage.js.map +1 -1
  20. package/dist/brainyrich/pages/GoalsPage.js +285 -285
  21. package/dist/brainyrich/pages/GoalsPage.js.map +1 -1
  22. package/dist/brainyrich/pages/InvestmentsPage.js +18 -18
  23. package/dist/brainyrich/pages/InvestmentsPage.js.map +1 -1
  24. package/dist/brainyrich/pages/PortfoliosPage.d.ts.map +1 -1
  25. package/dist/brainyrich/pages/PortfoliosPage.js +477 -471
  26. package/dist/brainyrich/pages/PortfoliosPage.js.map +1 -1
  27. package/dist/brainyrich/pages/ReportsPage.js +52 -52
  28. package/dist/brainyrich/pages/ReportsPage.js.map +1 -1
  29. package/dist/brainyrich/pages/RiskPage.d.ts.map +1 -1
  30. package/dist/brainyrich/pages/RiskPage.js +272 -271
  31. package/dist/brainyrich/pages/RiskPage.js.map +1 -1
  32. package/dist/brainyrich/pages/TaxPage.d.ts.map +1 -1
  33. package/dist/brainyrich/pages/TaxPage.js +198 -197
  34. package/dist/brainyrich/pages/TaxPage.js.map +1 -1
  35. package/dist/brainyrich/pages/_shared.d.ts.map +1 -1
  36. package/dist/brainyrich/pages/_shared.js +84 -82
  37. package/dist/brainyrich/pages/_shared.js.map +1 -1
  38. package/dist/brainyrich/types/bra.d.ts.map +1 -1
  39. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"InvestmentsPage.js","names":[],"sources":["../../../src/brainyrich/pages/InvestmentsPage.tsx"],"sourcesContent":["import { useMemo, useState } from 'react';\nimport {\n Card,\n CardContent,\n CardHeader,\n CardTitle,\n CardDescription,\n Badge,\n Button,\n Input,\n Label,\n Textarea,\n Select,\n SelectTrigger,\n SelectValue,\n SelectContent,\n SelectItem,\n Dialog,\n DialogContent,\n DialogHeader,\n DialogTitle,\n DialogDescription,\n DialogFooter,\n} from '@burdenoff/fe-libs/ui';\nimport { CheckCircle2, Gauge, Layers, PieChart, Plus, ShieldCheck, Trash2, X } from 'lucide-react';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { tWithFallback } from '../utils/i18n';\nimport {\n useModelPortfolios,\n useModelPortfolio,\n useCreateModelPortfolio,\n} from '../hooks/useBrainyRichApi';\nimport type {\n BraAssetClass,\n BraRiskProfile,\n BraModelAllocationInput,\n BraModelPortfolio,\n} from '../types/bra';\nimport {\n SectionShell,\n AllocationBars,\n EmptyState,\n LoadingState,\n ErrorState,\n formatDate,\n formatPct,\n statusBadgeVariant,\n type AllocationDatum,\n} from './_shared';\nimport {\n DonutChart,\n StatTile,\n ProgressRing,\n GlassCard,\n FadeIn,\n type DonutDatum,\n type VizTone,\n} from '../components';\n\n// ----------------------------------------------------------------------------\n// Enum option sets (mirror the SDL enums in ../types/bra) + humanizers.\n// No fabricated data: these are the exact allowed input values.\n// ----------------------------------------------------------------------------\n\nconst RISK_PROFILES: BraRiskProfile[] = [\n 'CONSERVATIVE',\n 'MODERATE',\n 'BALANCED',\n 'GROWTH',\n 'AGGRESSIVE',\n];\n\nconst ASSET_CLASSES: BraAssetClass[] = [\n 'EQUITY',\n 'FIXED_INCOME',\n 'CASH',\n 'REAL_ESTATE',\n 'COMMODITY',\n 'CRYPTO',\n 'ALTERNATIVE',\n];\n\n/** Turn an UPPER_SNAKE enum member into a readable label (derived, not invented). */\nfunction humanize(value: string): string {\n return value\n .toLowerCase()\n .split('_')\n .map((w) => w.charAt(0).toUpperCase() + w.slice(1))\n .join(' ');\n}\n\nconst ALLOCATION_TONES: AllocationDatum['tone'][] = [\n 'accent',\n 'default',\n 'positive',\n 'warning',\n 'negative',\n];\n\n/**\n * Stable, distinct viz tone per asset class so the same class reads the same\n * emerald/blue/teal/… hue across every donut on the page. Purely presentational.\n */\nconst ASSET_TONE: Record<BraAssetClass, VizTone> = {\n EQUITY: 'brand',\n FIXED_INCOME: 'blue',\n CASH: 'teal',\n REAL_ESTATE: 'purple',\n COMMODITY: 'warning',\n CRYPTO: 'indigo',\n ALTERNATIVE: 'info',\n};\n\n/** Build tone-mapped donut slices from a set of model allocations (real data only). */\nfunction toDonutData(\n allocations: { assetClass: BraAssetClass; targetPct: number }[]\n): DonutDatum[] {\n return allocations\n .filter((a) => Number.isFinite(a.targetPct) && a.targetPct > 0)\n .map((a) => ({\n label: humanize(a.assetClass),\n value: a.targetPct,\n tone: ASSET_TONE[a.assetClass],\n }));\n}\n\n/**\n * Blend the target allocations of a set of models into a single normalised\n * distribution (each asset class = its share of the summed targets). This is a\n * derived aggregate of REAL model data — no fabricated series.\n */\nfunction blendAllocations(models: BraModelPortfolio[]): DonutDatum[] {\n const totals = new Map<BraAssetClass, number>();\n for (const m of models) {\n for (const a of m.allocations) {\n if (!Number.isFinite(a.targetPct) || a.targetPct <= 0) continue;\n totals.set(a.assetClass, (totals.get(a.assetClass) ?? 0) + a.targetPct);\n }\n }\n const grand = Array.from(totals.values()).reduce((s, v) => s + v, 0);\n if (grand <= 0) return [];\n return Array.from(totals.entries())\n .map(([assetClass, sum]) => ({\n label: humanize(assetClass),\n value: (sum / grand) * 100,\n tone: ASSET_TONE[assetClass],\n }))\n .sort((a, b) => b.value - a.value);\n}\n\ninterface AllocationRow {\n assetClass: BraAssetClass;\n targetPct: string;\n}\n\nconst emptyRow = (): AllocationRow => ({ assetClass: 'EQUITY', targetPct: '' });\n\nexport function InvestmentsPage() {\n const { t } = useI18n();\n const translate = (key: string, fallback: string, params?: Record<string, string | number>) =>\n tWithFallback(t, key, fallback, params);\n\n const { data: models, isLoading, isError, refetch } = useModelPortfolios();\n const createModel = useCreateModelPortfolio();\n\n const [createOpen, setCreateOpen] = useState(false);\n const [detailId, setDetailId] = useState<string | null>(null);\n\n // -------- Create form state --------\n const [name, setName] = useState('');\n const [riskProfile, setRiskProfile] = useState<BraRiskProfile>('BALANCED');\n const [baseCurrency, setBaseCurrency] = useState('USD');\n const [description, setDescription] = useState('');\n const [rows, setRows] = useState<AllocationRow[]>([emptyRow()]);\n const [formError, setFormError] = useState<string | null>(null);\n\n const resetForm = () => {\n setName('');\n setRiskProfile('BALANCED');\n setBaseCurrency('USD');\n setDescription('');\n setRows([emptyRow()]);\n setFormError(null);\n };\n\n const allocationSum = useMemo(\n () =>\n rows.reduce(\n (s, r) => s + (Number.isFinite(parseFloat(r.targetPct)) ? parseFloat(r.targetPct) : 0),\n 0\n ),\n [rows]\n );\n\n // Live donut preview built from the current form rows (real, in-progress input).\n const previewDonut = useMemo<DonutDatum[]>(\n () =>\n toDonutData(\n rows.map((r) => ({ assetClass: r.assetClass, targetPct: parseFloat(r.targetPct) }))\n ),\n [rows]\n );\n const sumComplete = Math.abs(allocationSum - 100) <= 0.5;\n\n const updateRow = (index: number, patch: Partial<AllocationRow>) => {\n setRows((prev) => prev.map((r, i) => (i === index ? { ...r, ...patch } : r)));\n };\n const addRow = () => setRows((prev) => [...prev, emptyRow()]);\n const removeRow = (index: number) =>\n setRows((prev) => (prev.length > 1 ? prev.filter((_, i) => i !== index) : prev));\n\n const handleSubmit = () => {\n setFormError(null);\n const trimmedName = name.trim();\n if (!trimmedName) {\n setFormError(translate('brainyrich.investments.form.errorName', 'A model name is required.'));\n return;\n }\n const allocations: BraModelAllocationInput[] = [];\n for (const r of rows) {\n const pct = parseFloat(r.targetPct);\n if (!r.targetPct.trim() || !Number.isFinite(pct)) {\n setFormError(\n translate(\n 'brainyrich.investments.form.errorPct',\n 'Every allocation row needs a numeric target %.'\n )\n );\n return;\n }\n if (pct < 0) {\n setFormError(\n translate('brainyrich.investments.form.errorNegative', 'Target % cannot be negative.')\n );\n return;\n }\n allocations.push({ assetClass: r.assetClass, targetPct: pct });\n }\n if (Math.abs(allocationSum - 100) > 0.5) {\n setFormError(\n translate(\n 'brainyrich.investments.form.errorSum',\n 'Allocation targets must sum to ~100% (currently {sum}%).',\n {\n sum: allocationSum.toFixed(1),\n }\n )\n );\n return;\n }\n\n createModel.mutate(\n {\n name: trimmedName,\n riskProfile,\n baseCurrency: baseCurrency.trim() || 'USD',\n description: description.trim() ? description.trim() : null,\n allocations,\n },\n {\n onSuccess: () => {\n setCreateOpen(false);\n resetForm();\n },\n onError: (err: unknown) => {\n setFormError(\n err instanceof Error\n ? err.message\n : translate('brainyrich.investments.form.errorGeneric', 'Could not create the model.')\n );\n },\n }\n );\n };\n\n const title = translate('brainyrich.investments.title', 'Model Portfolios');\n const description2 = translate(\n 'brainyrich.investments.description',\n 'Reusable target allocations you can benchmark and drive rebalancing against'\n );\n\n const activeModels = models.filter((m) => m.isActive);\n const activeCount = activeModels.length;\n const riskCount = new Set(models.map((m) => m.riskProfile)).size;\n const classCount = new Set(models.flatMap((m) => m.allocations.map((a) => a.assetClass))).size;\n\n // Blended target mix across active models (falls back to all models). Derived\n // from real allocations — a headline aggregate, never fabricated.\n const blendSource = activeModels.length > 0 ? activeModels : models;\n const blended = useMemo(() => blendAllocations(blendSource), [blendSource]);\n\n const statTiles = [\n {\n label: translate('brainyrich.investments.kpi.total', 'Model portfolios'),\n value: String(models.length),\n icon: <Layers className=\"size-4\" aria-hidden=\"true\" />,\n glow: true,\n },\n {\n label: translate('brainyrich.investments.kpi.active', 'Active'),\n value: String(activeCount),\n icon: <CheckCircle2 className=\"size-4\" aria-hidden=\"true\" />,\n glow: false,\n },\n {\n label: translate('brainyrich.investments.kpi.risk', 'Risk profiles'),\n value: String(riskCount),\n icon: <Gauge className=\"size-4\" aria-hidden=\"true\" />,\n glow: false,\n },\n {\n label: translate('brainyrich.investments.kpi.classes', 'Asset classes'),\n value: String(classCount),\n icon: <PieChart className=\"size-4\" aria-hidden=\"true\" />,\n glow: false,\n },\n ];\n\n const newModelLabel = translate('brainyrich.investments.actions.newModel', 'New model');\n\n const renderBody = () => {\n if (isLoading) return <LoadingState rows={4} />;\n if (isError)\n return (\n <ErrorState\n title={translate('brainyrich.investments.error.title', 'Could not load model portfolios')}\n onRetry={() => refetch()}\n />\n );\n if (models.length === 0)\n return (\n <EmptyState\n icon={<PieChart className=\"size-6\" aria-hidden=\"true\" />}\n title={translate('brainyrich.investments.empty.title', 'No model portfolios yet')}\n description={translate(\n 'brainyrich.investments.empty.description',\n 'Create a target allocation model to benchmark portfolios and drive rebalancing.'\n )}\n action={\n <Button size=\"sm\" onClick={() => setCreateOpen(true)}>\n <Plus className=\"mr-1 size-4\" aria-hidden=\"true\" />\n {newModelLabel}\n </Button>\n }\n />\n );\n\n return (\n <div className=\"grid gap-4 md:grid-cols-2 xl:grid-cols-3\">\n {models.map((m, idx) => {\n const donut = toDonutData(m.allocations);\n const dominant = [...m.allocations].sort((a, b) => b.targetPct - a.targetPct)[0];\n const summary = m.allocations\n .map((a) => `${humanize(a.assetClass)} ${formatPct(a.targetPct, 0)}`)\n .join(' · ');\n return (\n <FadeIn key={m.id} index={idx} className=\"h-full\">\n <Card className=\"group flex h-full flex-col overflow-hidden border-border-subtle bg-bg-surface transition-shadow motion-safe:hover:shadow-[var(--shadow-pop-hover,var(--shadow-elevation-2))]\">\n <CardHeader className=\"space-y-2\">\n <div className=\"flex items-start justify-between gap-2\">\n <CardTitle className=\"text-base text-text-primary\">{m.name}</CardTitle>\n <Badge variant={m.isActive ? statusBadgeVariant('ACTIVE') : 'secondary'}>\n <span\n aria-hidden=\"true\"\n className={`mr-1 inline-block size-1.5 rounded-full ${\n m.isActive ? 'bg-status-success-text' : 'bg-text-secondary'\n }`}\n />\n {m.isActive\n ? translate('brainyrich.investments.badge.active', 'Active')\n : translate('brainyrich.investments.badge.inactive', 'Inactive')}\n </Badge>\n </div>\n <div className=\"flex flex-wrap items-center gap-2\">\n <Badge variant=\"outline\" className=\"gap-1\">\n <Gauge className=\"size-3\" aria-hidden=\"true\" />\n {humanize(m.riskProfile)}\n </Badge>\n <Badge variant=\"outline\">{m.baseCurrency}</Badge>\n </div>\n {m.description ? (\n <CardDescription className=\"line-clamp-2 text-text-secondary\">\n {m.description}\n </CardDescription>\n ) : null}\n </CardHeader>\n <CardContent className=\"mt-auto space-y-4\">\n {donut.length > 0 ? (\n <DonutChart\n data={donut}\n height={168}\n centerValue={dominant ? formatPct(dominant.targetPct, 0) : undefined}\n centerLabel={dominant ? humanize(dominant.assetClass) : undefined}\n valueFormatter={(v) => formatPct(v, 0)}\n />\n ) : (\n <p className=\"text-sm text-text-secondary\">\n {translate(\n 'brainyrich.investments.card.noAllocations',\n 'No target allocations set.'\n )}\n </p>\n )}\n {summary ? (\n <p className=\"truncate text-xs text-text-secondary\" title={summary}>\n {summary}\n </p>\n ) : null}\n <Button\n variant=\"outline\"\n size=\"sm\"\n className=\"w-full\"\n onClick={() => setDetailId(m.id)}\n >\n {translate('brainyrich.investments.actions.viewDetails', 'View details')}\n </Button>\n </CardContent>\n </Card>\n </FadeIn>\n );\n })}\n </div>\n );\n };\n\n return (\n <>\n <SectionShell\n title={title}\n description={description2}\n actionLabel={newModelLabel}\n onAction={() => setCreateOpen(true)}\n badges={\n !isLoading && !isError\n ? [\n translate('brainyrich.investments.badges.count', '{count} models', {\n count: models.length,\n }),\n ]\n : []\n }\n >\n {!isLoading && !isError && models.length > 0 ? (\n <FadeIn>\n <GlassCard glow className=\"p-5\">\n <div className=\"grid gap-6 lg:grid-cols-[minmax(0,0.85fr)_minmax(0,1.15fr)] lg:items-center\">\n <div className=\"space-y-3\">\n <div className=\"flex items-center gap-2\">\n <ShieldCheck className=\"size-4 text-status-success-text\" aria-hidden=\"true\" />\n <p className=\"text-xs font-medium uppercase tracking-wide text-text-secondary\">\n {translate(\n 'brainyrich.investments.hero.blended',\n 'Blended target mix — active models'\n )}\n </p>\n </div>\n {blended.length > 0 ? (\n <DonutChart\n data={blended}\n height={188}\n centerValue={String(activeCount || models.length)}\n centerLabel={translate('brainyrich.investments.hero.models', 'models')}\n valueFormatter={(v) => formatPct(v, 0)}\n />\n ) : (\n <p className=\"text-sm text-text-secondary\">\n {translate(\n 'brainyrich.investments.hero.noBlend',\n 'Add target allocations to see the blended mix.'\n )}\n </p>\n )}\n </div>\n <div className=\"grid grid-cols-2 gap-3\">\n {statTiles.map((tile) => (\n <StatTile\n key={tile.label}\n label={tile.label}\n value={tile.value}\n icon={tile.icon}\n glow={tile.glow}\n />\n ))}\n </div>\n </div>\n </GlassCard>\n </FadeIn>\n ) : null}\n {renderBody()}\n </SectionShell>\n\n {/* ---------------- Create model dialog ---------------- */}\n <Dialog\n open={createOpen}\n onOpenChange={(open) => {\n setCreateOpen(open);\n if (!open) resetForm();\n }}\n >\n <DialogContent className=\"max-h-[90vh] overflow-y-auto sm:max-w-lg\">\n <DialogHeader>\n <DialogTitle>{newModelLabel}</DialogTitle>\n <DialogDescription>\n {translate(\n 'brainyrich.investments.form.subtitle',\n 'Define a target allocation. Targets should sum to about 100%.'\n )}\n </DialogDescription>\n </DialogHeader>\n\n <div className=\"space-y-4 py-2\">\n <div className=\"space-y-2\">\n <Label htmlFor=\"bra-model-name\">\n {translate('brainyrich.investments.form.name', 'Name')}\n </Label>\n <Input\n id=\"bra-model-name\"\n value={name}\n onChange={(e) => setName(e.target.value)}\n placeholder={translate(\n 'brainyrich.investments.form.namePlaceholder',\n 'Balanced Growth 60/40'\n )}\n />\n </div>\n\n <div className=\"grid gap-4 sm:grid-cols-2\">\n <div className=\"space-y-2\">\n <Label htmlFor=\"bra-model-risk\">\n {translate('brainyrich.investments.form.risk', 'Risk profile')}\n </Label>\n <Select\n value={riskProfile}\n onValueChange={(v) => setRiskProfile(v as BraRiskProfile)}\n >\n <SelectTrigger id=\"bra-model-risk\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {RISK_PROFILES.map((r) => (\n <SelectItem key={r} value={r}>\n {humanize(r)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n <div className=\"space-y-2\">\n <Label htmlFor=\"bra-model-currency\">\n {translate('brainyrich.investments.form.currency', 'Base currency')}\n </Label>\n <Input\n id=\"bra-model-currency\"\n value={baseCurrency}\n onChange={(e) => setBaseCurrency(e.target.value.toUpperCase().slice(0, 3))}\n placeholder=\"USD\"\n />\n </div>\n </div>\n\n <div className=\"space-y-2\">\n <Label htmlFor=\"bra-model-desc\">\n {translate('brainyrich.investments.form.description', 'Description')}\n </Label>\n <Textarea\n id=\"bra-model-desc\"\n value={description}\n onChange={(e) => setDescription(e.target.value)}\n rows={2}\n placeholder={translate(\n 'brainyrich.investments.form.descriptionPlaceholder',\n 'Who this model is for and how it is used'\n )}\n />\n </div>\n\n <div className=\"space-y-2\">\n <div className=\"flex items-center justify-between\">\n <Label>\n {translate('brainyrich.investments.form.allocations', 'Target allocations')}\n </Label>\n <span\n className={`inline-flex items-center gap-1 rounded-full px-2 py-0.5 text-xs font-medium ${\n sumComplete\n ? 'bg-status-success-bg text-status-success-text'\n : 'bg-status-warning-bg text-status-warning-text'\n }`}\n >\n {sumComplete ? (\n <CheckCircle2 className=\"size-3\" aria-hidden=\"true\" />\n ) : (\n <span aria-hidden=\"true\">▲</span>\n )}\n {translate('brainyrich.investments.form.sum', 'Total: {sum}%', {\n sum: allocationSum.toFixed(allocationSum % 1 === 0 ? 0 : 1),\n })}\n </span>\n </div>\n\n {previewDonut.length > 0 ? (\n <div className=\"flex items-center gap-4 rounded-xl border border-border-subtle bg-bg-sunken/40 p-3\">\n <ProgressRing\n value={Math.min(100, allocationSum)}\n size={76}\n strokeWidth={8}\n tone={sumComplete ? 'success' : 'warning'}\n label={translate('brainyrich.investments.form.of100', 'of 100%')}\n />\n <div className=\"min-w-0 flex-1\">\n <DonutChart\n data={previewDonut}\n height={132}\n legend={false}\n valueFormatter={(v) => formatPct(v, 0)}\n />\n </div>\n </div>\n ) : null}\n\n <div className=\"space-y-2\">\n {rows.map((r, i) => (\n <div key={i} className=\"flex items-center gap-2\">\n <Select\n value={r.assetClass}\n onValueChange={(v) => updateRow(i, { assetClass: v as BraAssetClass })}\n >\n <SelectTrigger className=\"flex-1\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {ASSET_CLASSES.map((c) => (\n <SelectItem key={c} value={c}>\n {humanize(c)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n <Input\n type=\"number\"\n inputMode=\"decimal\"\n min={0}\n max={100}\n step=\"0.1\"\n className=\"w-24\"\n value={r.targetPct}\n onChange={(e) => updateRow(i, { targetPct: e.target.value })}\n placeholder=\"%\"\n aria-label={translate(\n 'brainyrich.investments.form.targetPct',\n 'Target percent'\n )}\n />\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n onClick={() => removeRow(i)}\n disabled={rows.length <= 1}\n aria-label={translate(\n 'brainyrich.investments.form.removeRow',\n 'Remove allocation row'\n )}\n >\n <Trash2 className=\"size-4\" aria-hidden=\"true\" />\n </Button>\n </div>\n ))}\n </div>\n <Button type=\"button\" variant=\"outline\" size=\"sm\" onClick={addRow}>\n <Plus className=\"mr-1 size-4\" aria-hidden=\"true\" />\n {translate('brainyrich.investments.form.addRow', 'Add asset class')}\n </Button>\n </div>\n\n {formError ? (\n <p className=\"text-sm text-status-error-text\" role=\"alert\">\n {formError}\n </p>\n ) : null}\n </div>\n\n <DialogFooter>\n <Button\n variant=\"outline\"\n onClick={() => {\n setCreateOpen(false);\n resetForm();\n }}\n disabled={createModel.isPending}\n >\n {translate('brainyrich.investments.form.cancel', 'Cancel')}\n </Button>\n <Button onClick={handleSubmit} disabled={createModel.isPending}>\n {createModel.isPending\n ? translate('brainyrich.investments.form.saving', 'Creating…')\n : translate('brainyrich.investments.form.create', 'Create model')}\n </Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n\n {/* ---------------- Detail dialog ---------------- */}\n <ModelDetailDialog id={detailId} onClose={() => setDetailId(null)} translate={translate} />\n </>\n );\n}\n\n// ----------------------------------------------------------------------------\n// Detail dialog — reads the single model via useModelPortfolio(id).\n// ----------------------------------------------------------------------------\n\nfunction ModelDetailDialog({\n id,\n onClose,\n translate,\n}: {\n id: string | null;\n onClose: () => void;\n translate: (key: string, fallback: string, params?: Record<string, string | number>) => string;\n}) {\n const { data: model, isLoading, isError, refetch } = useModelPortfolio(id ?? undefined);\n\n const bars: AllocationDatum[] = (model?.allocations ?? []).map((a, i) => ({\n label: humanize(a.assetClass),\n value: a.targetPct,\n tone: ALLOCATION_TONES[i % ALLOCATION_TONES.length],\n }));\n const donut = toDonutData(model?.allocations ?? []);\n const dominant = [...(model?.allocations ?? [])].sort((a, b) => b.targetPct - a.targetPct)[0];\n\n return (\n <Dialog open={Boolean(id)} onOpenChange={(open) => (!open ? onClose() : undefined)}>\n <DialogContent className=\"max-h-[90vh] overflow-y-auto sm:max-w-lg\">\n <DialogHeader>\n <DialogTitle className=\"flex items-center justify-between gap-2 pr-6\">\n {model?.name ?? translate('brainyrich.investments.detail.title', 'Model portfolio')}\n </DialogTitle>\n {model?.description ? <DialogDescription>{model.description}</DialogDescription> : null}\n </DialogHeader>\n\n {isLoading ? (\n <LoadingState rows={3} />\n ) : isError ? (\n <ErrorState\n title={translate('brainyrich.investments.detail.error', 'Could not load this model')}\n onRetry={() => refetch()}\n />\n ) : !model ? (\n <EmptyState\n icon={<X className=\"size-6\" aria-hidden=\"true\" />}\n title={translate('brainyrich.investments.detail.notFound', 'Model not found')}\n />\n ) : (\n <div className=\"space-y-5 py-1\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <Badge variant={model.isActive ? statusBadgeVariant('ACTIVE') : 'secondary'}>\n {model.isActive\n ? translate('brainyrich.investments.badge.active', 'Active')\n : translate('brainyrich.investments.badge.inactive', 'Inactive')}\n </Badge>\n <Badge variant=\"outline\">{humanize(model.riskProfile)}</Badge>\n <Badge variant=\"outline\">{model.baseCurrency}</Badge>\n </div>\n\n <div>\n <p className=\"mb-2 text-sm font-medium text-text-primary\">\n {translate('brainyrich.investments.detail.allocations', 'Target allocations')}\n </p>\n {bars.length > 0 ? (\n <div className=\"space-y-4\">\n {donut.length > 0 ? (\n <div className=\"rounded-xl border border-border-subtle bg-bg-sunken/40 p-3\">\n <DonutChart\n data={donut}\n height={180}\n centerValue={dominant ? formatPct(dominant.targetPct, 0) : undefined}\n centerLabel={dominant ? humanize(dominant.assetClass) : undefined}\n valueFormatter={(v) => formatPct(v, 0)}\n />\n </div>\n ) : null}\n <AllocationBars data={bars} />\n </div>\n ) : (\n <p className=\"text-sm text-text-secondary\">\n {translate(\n 'brainyrich.investments.card.noAllocations',\n 'No target allocations set.'\n )}\n </p>\n )}\n </div>\n\n <dl className=\"grid grid-cols-2 gap-4 border-t border-border-subtle pt-4 text-sm\">\n <div>\n <dt className=\"text-text-secondary\">\n {translate('brainyrich.investments.detail.created', 'Created')}\n </dt>\n <dd className=\"text-text-primary\">{formatDate(model.createdAt)}</dd>\n </div>\n <div>\n <dt className=\"text-text-secondary\">\n {translate('brainyrich.investments.detail.updated', 'Updated')}\n </dt>\n <dd className=\"text-text-primary\">{formatDate(model.updatedAt)}</dd>\n </div>\n </dl>\n </div>\n )}\n\n <DialogFooter>\n <Button variant=\"outline\" onClick={onClose}>\n {translate('brainyrich.investments.detail.close', 'Close')}\n </Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"InvestmentsPage.js","names":[],"sources":["../../../src/brainyrich/pages/InvestmentsPage.tsx"],"sourcesContent":["import { useMemo, useState } from 'react';\nimport {\n Card,\n CardContent,\n CardHeader,\n CardTitle,\n CardDescription,\n Badge,\n Button,\n Input,\n Label,\n Textarea,\n Select,\n SelectTrigger,\n SelectValue,\n SelectContent,\n SelectItem,\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n ResponsiveDialogDescription,\n ResponsiveDialogFooter,\n} from '@burdenoff/fe-libs/ui';\nimport { CheckCircle2, Gauge, Layers, PieChart, Plus, ShieldCheck, Trash2, X } from 'lucide-react';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { tWithFallback } from '../utils/i18n';\nimport {\n useModelPortfolios,\n useModelPortfolio,\n useCreateModelPortfolio,\n} from '../hooks/useBrainyRichApi';\nimport type {\n BraAssetClass,\n BraRiskProfile,\n BraModelAllocationInput,\n BraModelPortfolio,\n} from '../types/bra';\nimport {\n SectionShell,\n AllocationBars,\n EmptyState,\n LoadingState,\n ErrorState,\n formatDate,\n formatPct,\n statusBadgeVariant,\n type AllocationDatum,\n} from './_shared';\nimport {\n DonutChart,\n StatTile,\n ProgressRing,\n GlassCard,\n FadeIn,\n type DonutDatum,\n type VizTone,\n} from '../components';\n\n// ----------------------------------------------------------------------------\n// Enum option sets (mirror the SDL enums in ../types/bra) + humanizers.\n// No fabricated data: these are the exact allowed input values.\n// ----------------------------------------------------------------------------\n\nconst RISK_PROFILES: BraRiskProfile[] = [\n 'CONSERVATIVE',\n 'MODERATE',\n 'BALANCED',\n 'GROWTH',\n 'AGGRESSIVE',\n];\n\nconst ASSET_CLASSES: BraAssetClass[] = [\n 'EQUITY',\n 'FIXED_INCOME',\n 'CASH',\n 'REAL_ESTATE',\n 'COMMODITY',\n 'CRYPTO',\n 'ALTERNATIVE',\n];\n\n/** Turn an UPPER_SNAKE enum member into a readable label (derived, not invented). */\nfunction humanize(value: string): string {\n return value\n .toLowerCase()\n .split('_')\n .map((w) => w.charAt(0).toUpperCase() + w.slice(1))\n .join(' ');\n}\n\nconst ALLOCATION_TONES: AllocationDatum['tone'][] = [\n 'accent',\n 'default',\n 'positive',\n 'warning',\n 'negative',\n];\n\n/**\n * Stable, distinct viz tone per asset class so the same class reads the same\n * emerald/blue/teal/… hue across every donut on the page. Purely presentational.\n */\nconst ASSET_TONE: Record<BraAssetClass, VizTone> = {\n EQUITY: 'brand',\n FIXED_INCOME: 'blue',\n CASH: 'teal',\n REAL_ESTATE: 'purple',\n COMMODITY: 'warning',\n CRYPTO: 'indigo',\n ALTERNATIVE: 'info',\n};\n\n/** Build tone-mapped donut slices from a set of model allocations (real data only). */\nfunction toDonutData(\n allocations: { assetClass: BraAssetClass; targetPct: number }[]\n): DonutDatum[] {\n return allocations\n .filter((a) => Number.isFinite(a.targetPct) && a.targetPct > 0)\n .map((a) => ({\n label: humanize(a.assetClass),\n value: a.targetPct,\n tone: ASSET_TONE[a.assetClass],\n }));\n}\n\n/**\n * Blend the target allocations of a set of models into a single normalised\n * distribution (each asset class = its share of the summed targets). This is a\n * derived aggregate of REAL model data — no fabricated series.\n */\nfunction blendAllocations(models: BraModelPortfolio[]): DonutDatum[] {\n const totals = new Map<BraAssetClass, number>();\n for (const m of models) {\n for (const a of m.allocations) {\n if (!Number.isFinite(a.targetPct) || a.targetPct <= 0) continue;\n totals.set(a.assetClass, (totals.get(a.assetClass) ?? 0) + a.targetPct);\n }\n }\n const grand = Array.from(totals.values()).reduce((s, v) => s + v, 0);\n if (grand <= 0) return [];\n return Array.from(totals.entries())\n .map(([assetClass, sum]) => ({\n label: humanize(assetClass),\n value: (sum / grand) * 100,\n tone: ASSET_TONE[assetClass],\n }))\n .sort((a, b) => b.value - a.value);\n}\n\ninterface AllocationRow {\n assetClass: BraAssetClass;\n targetPct: string;\n}\n\nconst emptyRow = (): AllocationRow => ({ assetClass: 'EQUITY', targetPct: '' });\n\nexport function InvestmentsPage() {\n const { t } = useI18n();\n const translate = (key: string, fallback: string, params?: Record<string, string | number>) =>\n tWithFallback(t, key, fallback, params);\n\n const { data: models, isLoading, isError, refetch } = useModelPortfolios();\n const createModel = useCreateModelPortfolio();\n\n const [createOpen, setCreateOpen] = useState(false);\n const [detailId, setDetailId] = useState<string | null>(null);\n\n // -------- Create form state --------\n const [name, setName] = useState('');\n const [riskProfile, setRiskProfile] = useState<BraRiskProfile>('BALANCED');\n const [baseCurrency, setBaseCurrency] = useState('USD');\n const [description, setDescription] = useState('');\n const [rows, setRows] = useState<AllocationRow[]>([emptyRow()]);\n const [formError, setFormError] = useState<string | null>(null);\n\n const resetForm = () => {\n setName('');\n setRiskProfile('BALANCED');\n setBaseCurrency('USD');\n setDescription('');\n setRows([emptyRow()]);\n setFormError(null);\n };\n\n const allocationSum = useMemo(\n () =>\n rows.reduce(\n (s, r) => s + (Number.isFinite(parseFloat(r.targetPct)) ? parseFloat(r.targetPct) : 0),\n 0\n ),\n [rows]\n );\n\n // Live donut preview built from the current form rows (real, in-progress input).\n const previewDonut = useMemo<DonutDatum[]>(\n () =>\n toDonutData(\n rows.map((r) => ({ assetClass: r.assetClass, targetPct: parseFloat(r.targetPct) }))\n ),\n [rows]\n );\n const sumComplete = Math.abs(allocationSum - 100) <= 0.5;\n\n const updateRow = (index: number, patch: Partial<AllocationRow>) => {\n setRows((prev) => prev.map((r, i) => (i === index ? { ...r, ...patch } : r)));\n };\n const addRow = () => setRows((prev) => [...prev, emptyRow()]);\n const removeRow = (index: number) =>\n setRows((prev) => (prev.length > 1 ? prev.filter((_, i) => i !== index) : prev));\n\n const handleSubmit = () => {\n setFormError(null);\n const trimmedName = name.trim();\n if (!trimmedName) {\n setFormError(translate('brainyrich.investments.form.errorName', 'A model name is required.'));\n return;\n }\n const allocations: BraModelAllocationInput[] = [];\n for (const r of rows) {\n const pct = parseFloat(r.targetPct);\n if (!r.targetPct.trim() || !Number.isFinite(pct)) {\n setFormError(\n translate(\n 'brainyrich.investments.form.errorPct',\n 'Every allocation row needs a numeric target %.'\n )\n );\n return;\n }\n if (pct < 0) {\n setFormError(\n translate('brainyrich.investments.form.errorNegative', 'Target % cannot be negative.')\n );\n return;\n }\n allocations.push({ assetClass: r.assetClass, targetPct: pct });\n }\n if (Math.abs(allocationSum - 100) > 0.5) {\n setFormError(\n translate(\n 'brainyrich.investments.form.errorSum',\n 'Allocation targets must sum to ~100% (currently {sum}%).',\n {\n sum: allocationSum.toFixed(1),\n }\n )\n );\n return;\n }\n\n createModel.mutate(\n {\n name: trimmedName,\n riskProfile,\n baseCurrency: baseCurrency.trim() || 'USD',\n description: description.trim() ? description.trim() : null,\n allocations,\n },\n {\n onSuccess: () => {\n setCreateOpen(false);\n resetForm();\n },\n onError: (err: unknown) => {\n setFormError(\n err instanceof Error\n ? err.message\n : translate('brainyrich.investments.form.errorGeneric', 'Could not create the model.')\n );\n },\n }\n );\n };\n\n const title = translate('brainyrich.investments.title', 'Model Portfolios');\n const description2 = translate(\n 'brainyrich.investments.description',\n 'Reusable target allocations you can benchmark and drive rebalancing against'\n );\n\n const activeModels = models.filter((m) => m.isActive);\n const activeCount = activeModels.length;\n const riskCount = new Set(models.map((m) => m.riskProfile)).size;\n const classCount = new Set(models.flatMap((m) => m.allocations.map((a) => a.assetClass))).size;\n\n // Blended target mix across active models (falls back to all models). Derived\n // from real allocations — a headline aggregate, never fabricated.\n const blendSource = activeModels.length > 0 ? activeModels : models;\n const blended = useMemo(() => blendAllocations(blendSource), [blendSource]);\n\n const statTiles = [\n {\n label: translate('brainyrich.investments.kpi.total', 'Model portfolios'),\n value: String(models.length),\n icon: <Layers className=\"size-4\" aria-hidden=\"true\" />,\n glow: true,\n },\n {\n label: translate('brainyrich.investments.kpi.active', 'Active'),\n value: String(activeCount),\n icon: <CheckCircle2 className=\"size-4\" aria-hidden=\"true\" />,\n glow: false,\n },\n {\n label: translate('brainyrich.investments.kpi.risk', 'Risk profiles'),\n value: String(riskCount),\n icon: <Gauge className=\"size-4\" aria-hidden=\"true\" />,\n glow: false,\n },\n {\n label: translate('brainyrich.investments.kpi.classes', 'Asset classes'),\n value: String(classCount),\n icon: <PieChart className=\"size-4\" aria-hidden=\"true\" />,\n glow: false,\n },\n ];\n\n const newModelLabel = translate('brainyrich.investments.actions.newModel', 'New model');\n\n const renderBody = () => {\n if (isLoading) return <LoadingState rows={4} />;\n if (isError)\n return (\n <ErrorState\n title={translate('brainyrich.investments.error.title', 'Could not load model portfolios')}\n onRetry={() => refetch()}\n />\n );\n if (models.length === 0)\n return (\n <EmptyState\n icon={<PieChart className=\"size-6\" aria-hidden=\"true\" />}\n title={translate('brainyrich.investments.empty.title', 'No model portfolios yet')}\n description={translate(\n 'brainyrich.investments.empty.description',\n 'Create a target allocation model to benchmark portfolios and drive rebalancing.'\n )}\n action={\n <Button size=\"sm\" onClick={() => setCreateOpen(true)}>\n <Plus className=\"mr-1 size-4\" aria-hidden=\"true\" />\n {newModelLabel}\n </Button>\n }\n />\n );\n\n return (\n <div className=\"grid gap-4 md:grid-cols-2 xl:grid-cols-3\">\n {models.map((m, idx) => {\n const donut = toDonutData(m.allocations);\n const dominant = [...m.allocations].sort((a, b) => b.targetPct - a.targetPct)[0];\n const summary = m.allocations\n .map((a) => `${humanize(a.assetClass)} ${formatPct(a.targetPct, 0)}`)\n .join(' · ');\n return (\n <FadeIn key={m.id} index={idx} className=\"h-full\">\n <Card className=\"group flex h-full flex-col overflow-hidden border-border-subtle bg-bg-surface transition-shadow motion-safe:hover:shadow-[var(--shadow-pop-hover,var(--shadow-elevation-2))]\">\n <CardHeader className=\"space-y-2\">\n <div className=\"flex items-start justify-between gap-2\">\n <CardTitle className=\"text-base text-text-primary\">{m.name}</CardTitle>\n <Badge variant={m.isActive ? statusBadgeVariant('ACTIVE') : 'secondary'}>\n <span\n aria-hidden=\"true\"\n className={`mr-1 inline-block size-1.5 rounded-full ${\n m.isActive ? 'bg-status-success-text' : 'bg-text-secondary'\n }`}\n />\n {m.isActive\n ? translate('brainyrich.investments.badge.active', 'Active')\n : translate('brainyrich.investments.badge.inactive', 'Inactive')}\n </Badge>\n </div>\n <div className=\"flex flex-wrap items-center gap-2\">\n <Badge variant=\"outline\" className=\"gap-1\">\n <Gauge className=\"size-3\" aria-hidden=\"true\" />\n {humanize(m.riskProfile)}\n </Badge>\n <Badge variant=\"outline\">{m.baseCurrency}</Badge>\n </div>\n {m.description ? (\n <CardDescription className=\"line-clamp-2 text-text-secondary\">\n {m.description}\n </CardDescription>\n ) : null}\n </CardHeader>\n <CardContent className=\"mt-auto space-y-4\">\n {donut.length > 0 ? (\n <DonutChart\n data={donut}\n height={168}\n centerValue={dominant ? formatPct(dominant.targetPct, 0) : undefined}\n centerLabel={dominant ? humanize(dominant.assetClass) : undefined}\n valueFormatter={(v) => formatPct(v, 0)}\n />\n ) : (\n <p className=\"text-sm text-text-secondary\">\n {translate(\n 'brainyrich.investments.card.noAllocations',\n 'No target allocations set.'\n )}\n </p>\n )}\n {summary ? (\n <p className=\"truncate text-xs text-text-secondary\" title={summary}>\n {summary}\n </p>\n ) : null}\n <Button\n variant=\"outline\"\n size=\"sm\"\n className=\"w-full\"\n onClick={() => setDetailId(m.id)}\n >\n {translate('brainyrich.investments.actions.viewDetails', 'View details')}\n </Button>\n </CardContent>\n </Card>\n </FadeIn>\n );\n })}\n </div>\n );\n };\n\n return (\n <>\n <SectionShell\n title={title}\n description={description2}\n actionLabel={newModelLabel}\n onAction={() => setCreateOpen(true)}\n badges={\n !isLoading && !isError\n ? [\n translate('brainyrich.investments.badges.count', '{count} models', {\n count: models.length,\n }),\n ]\n : []\n }\n >\n {!isLoading && !isError && models.length > 0 ? (\n <FadeIn>\n <GlassCard glow className=\"p-5\">\n <div className=\"grid gap-6 lg:grid-cols-[minmax(0,0.85fr)_minmax(0,1.15fr)] lg:items-center\">\n <div className=\"space-y-3\">\n <div className=\"flex items-center gap-2\">\n <ShieldCheck className=\"size-4 text-status-success-text\" aria-hidden=\"true\" />\n <p className=\"text-xs font-medium uppercase tracking-wide text-text-secondary\">\n {translate(\n 'brainyrich.investments.hero.blended',\n 'Blended target mix — active models'\n )}\n </p>\n </div>\n {blended.length > 0 ? (\n <DonutChart\n data={blended}\n height={188}\n centerValue={String(activeCount || models.length)}\n centerLabel={translate('brainyrich.investments.hero.models', 'models')}\n valueFormatter={(v) => formatPct(v, 0)}\n />\n ) : (\n <p className=\"text-sm text-text-secondary\">\n {translate(\n 'brainyrich.investments.hero.noBlend',\n 'Add target allocations to see the blended mix.'\n )}\n </p>\n )}\n </div>\n <div className=\"grid grid-cols-2 gap-3\">\n {statTiles.map((tile) => (\n <StatTile\n key={tile.label}\n label={tile.label}\n value={tile.value}\n icon={tile.icon}\n glow={tile.glow}\n />\n ))}\n </div>\n </div>\n </GlassCard>\n </FadeIn>\n ) : null}\n {renderBody()}\n </SectionShell>\n\n {/* ---------------- Create model dialog ---------------- */}\n <ResponsiveDialog\n open={createOpen}\n onOpenChange={(open) => {\n setCreateOpen(open);\n if (!open) resetForm();\n }}\n >\n <ResponsiveDialogContent className=\"max-h-[90vh] overflow-y-auto sm:max-w-lg\">\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>{newModelLabel}</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n {translate(\n 'brainyrich.investments.form.subtitle',\n 'Define a target allocation. Targets should sum to about 100%.'\n )}\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n\n <div className=\"space-y-4 py-2\">\n <div className=\"space-y-2\">\n <Label htmlFor=\"bra-model-name\">\n {translate('brainyrich.investments.form.name', 'Name')}\n </Label>\n <Input\n id=\"bra-model-name\"\n value={name}\n onChange={(e) => setName(e.target.value)}\n placeholder={translate(\n 'brainyrich.investments.form.namePlaceholder',\n 'Balanced Growth 60/40'\n )}\n />\n </div>\n\n <div className=\"grid gap-4 sm:grid-cols-2\">\n <div className=\"space-y-2\">\n <Label htmlFor=\"bra-model-risk\">\n {translate('brainyrich.investments.form.risk', 'Risk profile')}\n </Label>\n <Select\n value={riskProfile}\n onValueChange={(v) => setRiskProfile(v as BraRiskProfile)}\n >\n <SelectTrigger id=\"bra-model-risk\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {RISK_PROFILES.map((r) => (\n <SelectItem key={r} value={r}>\n {humanize(r)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n <div className=\"space-y-2\">\n <Label htmlFor=\"bra-model-currency\">\n {translate('brainyrich.investments.form.currency', 'Base currency')}\n </Label>\n <Input\n id=\"bra-model-currency\"\n value={baseCurrency}\n onChange={(e) => setBaseCurrency(e.target.value.toUpperCase().slice(0, 3))}\n placeholder=\"USD\"\n />\n </div>\n </div>\n\n <div className=\"space-y-2\">\n <Label htmlFor=\"bra-model-desc\">\n {translate('brainyrich.investments.form.description', 'Description')}\n </Label>\n <Textarea\n id=\"bra-model-desc\"\n value={description}\n onChange={(e) => setDescription(e.target.value)}\n rows={2}\n placeholder={translate(\n 'brainyrich.investments.form.descriptionPlaceholder',\n 'Who this model is for and how it is used'\n )}\n />\n </div>\n\n <div className=\"space-y-2\">\n <div className=\"flex items-center justify-between\">\n <Label>\n {translate('brainyrich.investments.form.allocations', 'Target allocations')}\n </Label>\n <span\n className={`inline-flex items-center gap-1 rounded-full px-2 py-0.5 text-xs font-medium ${\n sumComplete\n ? 'bg-status-success-bg text-status-success-text'\n : 'bg-status-warning-bg text-status-warning-text'\n }`}\n >\n {sumComplete ? (\n <CheckCircle2 className=\"size-3\" aria-hidden=\"true\" />\n ) : (\n <span aria-hidden=\"true\">▲</span>\n )}\n {translate('brainyrich.investments.form.sum', 'Total: {sum}%', {\n sum: allocationSum.toFixed(allocationSum % 1 === 0 ? 0 : 1),\n })}\n </span>\n </div>\n\n {previewDonut.length > 0 ? (\n <div className=\"flex items-center gap-4 rounded-xl border border-border-subtle bg-bg-sunken/40 p-3\">\n <ProgressRing\n value={Math.min(100, allocationSum)}\n size={76}\n strokeWidth={8}\n tone={sumComplete ? 'success' : 'warning'}\n label={translate('brainyrich.investments.form.of100', 'of 100%')}\n />\n <div className=\"min-w-0 flex-1\">\n <DonutChart\n data={previewDonut}\n height={132}\n legend={false}\n valueFormatter={(v) => formatPct(v, 0)}\n />\n </div>\n </div>\n ) : null}\n\n <div className=\"space-y-2\">\n {rows.map((r, i) => (\n <div key={i} className=\"flex items-center gap-2\">\n <Select\n value={r.assetClass}\n onValueChange={(v) => updateRow(i, { assetClass: v as BraAssetClass })}\n >\n <SelectTrigger className=\"flex-1\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {ASSET_CLASSES.map((c) => (\n <SelectItem key={c} value={c}>\n {humanize(c)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n <Input\n type=\"number\"\n inputMode=\"decimal\"\n min={0}\n max={100}\n step=\"0.1\"\n className=\"w-24\"\n value={r.targetPct}\n onChange={(e) => updateRow(i, { targetPct: e.target.value })}\n placeholder=\"%\"\n aria-label={translate(\n 'brainyrich.investments.form.targetPct',\n 'Target percent'\n )}\n />\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n onClick={() => removeRow(i)}\n disabled={rows.length <= 1}\n aria-label={translate(\n 'brainyrich.investments.form.removeRow',\n 'Remove allocation row'\n )}\n >\n <Trash2 className=\"size-4\" aria-hidden=\"true\" />\n </Button>\n </div>\n ))}\n </div>\n <Button type=\"button\" variant=\"outline\" size=\"sm\" onClick={addRow}>\n <Plus className=\"mr-1 size-4\" aria-hidden=\"true\" />\n {translate('brainyrich.investments.form.addRow', 'Add asset class')}\n </Button>\n </div>\n\n {formError ? (\n <p className=\"text-sm text-status-error-text\" role=\"alert\">\n {formError}\n </p>\n ) : null}\n </div>\n\n <ResponsiveDialogFooter>\n <Button\n variant=\"outline\"\n onClick={() => {\n setCreateOpen(false);\n resetForm();\n }}\n disabled={createModel.isPending}\n >\n {translate('brainyrich.investments.form.cancel', 'Cancel')}\n </Button>\n <Button onClick={handleSubmit} disabled={createModel.isPending}>\n {createModel.isPending\n ? translate('brainyrich.investments.form.saving', 'Creating…')\n : translate('brainyrich.investments.form.create', 'Create model')}\n </Button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n\n {/* ---------------- Detail dialog ---------------- */}\n <ModelDetailDialog id={detailId} onClose={() => setDetailId(null)} translate={translate} />\n </>\n );\n}\n\n// ----------------------------------------------------------------------------\n// Detail dialog — reads the single model via useModelPortfolio(id).\n// ----------------------------------------------------------------------------\n\nfunction ModelDetailDialog({\n id,\n onClose,\n translate,\n}: {\n id: string | null;\n onClose: () => void;\n translate: (key: string, fallback: string, params?: Record<string, string | number>) => string;\n}) {\n const { data: model, isLoading, isError, refetch } = useModelPortfolio(id ?? undefined);\n\n const bars: AllocationDatum[] = (model?.allocations ?? []).map((a, i) => ({\n label: humanize(a.assetClass),\n value: a.targetPct,\n tone: ALLOCATION_TONES[i % ALLOCATION_TONES.length],\n }));\n const donut = toDonutData(model?.allocations ?? []);\n const dominant = [...(model?.allocations ?? [])].sort((a, b) => b.targetPct - a.targetPct)[0];\n\n return (\n <ResponsiveDialog open={Boolean(id)} onOpenChange={(open) => (!open ? onClose() : undefined)}>\n <ResponsiveDialogContent className=\"max-h-[90vh] overflow-y-auto sm:max-w-lg\">\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle className=\"flex items-center justify-between gap-2 pr-6\">\n {model?.name ?? translate('brainyrich.investments.detail.title', 'Model portfolio')}\n </ResponsiveDialogTitle>\n {model?.description ? (\n <ResponsiveDialogDescription>{model.description}</ResponsiveDialogDescription>\n ) : null}\n </ResponsiveDialogHeader>\n\n {isLoading ? (\n <LoadingState rows={3} />\n ) : isError ? (\n <ErrorState\n title={translate('brainyrich.investments.detail.error', 'Could not load this model')}\n onRetry={() => refetch()}\n />\n ) : !model ? (\n <EmptyState\n icon={<X className=\"size-6\" aria-hidden=\"true\" />}\n title={translate('brainyrich.investments.detail.notFound', 'Model not found')}\n />\n ) : (\n <div className=\"space-y-5 py-1\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <Badge variant={model.isActive ? statusBadgeVariant('ACTIVE') : 'secondary'}>\n {model.isActive\n ? translate('brainyrich.investments.badge.active', 'Active')\n : translate('brainyrich.investments.badge.inactive', 'Inactive')}\n </Badge>\n <Badge variant=\"outline\">{humanize(model.riskProfile)}</Badge>\n <Badge variant=\"outline\">{model.baseCurrency}</Badge>\n </div>\n\n <div>\n <p className=\"mb-2 text-sm font-medium text-text-primary\">\n {translate('brainyrich.investments.detail.allocations', 'Target allocations')}\n </p>\n {bars.length > 0 ? (\n <div className=\"space-y-4\">\n {donut.length > 0 ? (\n <div className=\"rounded-xl border border-border-subtle bg-bg-sunken/40 p-3\">\n <DonutChart\n data={donut}\n height={180}\n centerValue={dominant ? formatPct(dominant.targetPct, 0) : undefined}\n centerLabel={dominant ? humanize(dominant.assetClass) : undefined}\n valueFormatter={(v) => formatPct(v, 0)}\n />\n </div>\n ) : null}\n <AllocationBars data={bars} />\n </div>\n ) : (\n <p className=\"text-sm text-text-secondary\">\n {translate(\n 'brainyrich.investments.card.noAllocations',\n 'No target allocations set.'\n )}\n </p>\n )}\n </div>\n\n <dl className=\"grid grid-cols-2 gap-4 border-t border-border-subtle pt-4 text-sm\">\n <div>\n <dt className=\"text-text-secondary\">\n {translate('brainyrich.investments.detail.created', 'Created')}\n </dt>\n <dd className=\"text-text-primary\">{formatDate(model.createdAt)}</dd>\n </div>\n <div>\n <dt className=\"text-text-secondary\">\n {translate('brainyrich.investments.detail.updated', 'Updated')}\n </dt>\n <dd className=\"text-text-primary\">{formatDate(model.updatedAt)}</dd>\n </div>\n </dl>\n </div>\n )}\n\n <ResponsiveDialogFooter>\n <Button variant=\"outline\" onClick={onClose}>\n {translate('brainyrich.investments.detail.close', 'Close')}\n </Button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n );\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"PortfoliosPage.d.ts","sourceRoot":"","sources":["../../../src/brainyrich/pages/PortfoliosPage.tsx"],"names":[],"mappings":"AAkMA,MAAM,WAAW,kBAAkB;IACjC,qEAAqE;IACrE,WAAW,EAAE,MAAM,CAAC;IACpB,2BAA2B;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IACvB,qDAAqD;IACrD,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC;IACrB,uFAAuF;IACvF,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,8CAA8C;IAC9C,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;CAChC;AAqpCD,wBAAgB,cAAc,4CA6N7B"}
1
+ {"version":3,"file":"PortfoliosPage.d.ts","sourceRoot":"","sources":["../../../src/brainyrich/pages/PortfoliosPage.tsx"],"names":[],"mappings":"AAmMA,MAAM,WAAW,kBAAkB;IACjC,qEAAqE;IACrE,WAAW,EAAE,MAAM,CAAC;IACpB,2BAA2B;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IACvB,qDAAqD;IACrD,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC;IACrB,uFAAuF;IACvF,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,8CAA8C;IAC9C,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;CAChC;AA6sCD,wBAAgB,cAAc,4CA0Q7B"}