@burdenoff/microfe-brainyrich 2026.805.1 → 2026.825.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.
- package/dist/_virtual/_rolldown/runtime.js +1 -1
- package/dist/brainyrich/BrainyRichRoot.d.ts +0 -1
- package/dist/brainyrich/BrainyRichRoot.js.map +1 -1
- package/dist/brainyrich/BrainyRichRoutes.d.ts.map +1 -1
- package/dist/brainyrich/BrainyRichRoutes.js +21 -17
- package/dist/brainyrich/BrainyRichRoutes.js.map +1 -1
- package/dist/brainyrich/components/ActorName.js.map +1 -1
- package/dist/brainyrich/components/ConfirmDialog.d.ts +0 -1
- package/dist/brainyrich/components/ConfirmDialog.js.map +1 -1
- package/dist/brainyrich/components/LoadMore.js.map +1 -1
- package/dist/brainyrich/components/notify.js.map +1 -1
- package/dist/brainyrich/components/pickers.js.map +1 -1
- package/dist/brainyrich/components/reuse/CommentThread.js.map +1 -1
- package/dist/brainyrich/components/reuse/DocumentsPanel.js.map +1 -1
- package/dist/brainyrich/components/reuse/TagField.js.map +1 -1
- package/dist/brainyrich/components/reuse/transport.js.map +1 -1
- package/dist/brainyrich/components/viz.d.ts +0 -1
- package/dist/brainyrich/components/viz.js.map +1 -1
- package/dist/brainyrich/hooks/useBrainyRichAnalytics.js.map +1 -1
- package/dist/brainyrich/hooks/useBrainyRichApi.d.ts +3 -2
- package/dist/brainyrich/hooks/useBrainyRichApi.d.ts.map +1 -1
- package/dist/brainyrich/hooks/useBrainyRichApi.js +20 -12
- package/dist/brainyrich/hooks/useBrainyRichApi.js.map +1 -1
- package/dist/brainyrich/pages/AccountsPage.d.ts.map +1 -1
- package/dist/brainyrich/pages/AccountsPage.js +367 -363
- package/dist/brainyrich/pages/AccountsPage.js.map +1 -1
- package/dist/brainyrich/pages/AdminFirmPage.d.ts +2 -0
- package/dist/brainyrich/pages/AdminFirmPage.d.ts.map +1 -0
- package/dist/brainyrich/pages/AdminFirmPage.js +137 -0
- package/dist/brainyrich/pages/AdminFirmPage.js.map +1 -0
- package/dist/brainyrich/pages/AiAssistantPage.js.map +1 -1
- package/dist/brainyrich/pages/AnalyticsPage.js.map +1 -1
- package/dist/brainyrich/pages/DashboardPage.js.map +1 -1
- package/dist/brainyrich/pages/FamilyPage.js.map +1 -1
- package/dist/brainyrich/pages/FirmPage.js.map +1 -1
- package/dist/brainyrich/pages/GoalsPage.js.map +1 -1
- package/dist/brainyrich/pages/InvestmentsPage.js.map +1 -1
- package/dist/brainyrich/pages/PortfoliosPage.js.map +1 -1
- package/dist/brainyrich/pages/ReportsPage.js.map +1 -1
- package/dist/brainyrich/pages/RiskPage.js.map +1 -1
- package/dist/brainyrich/pages/TaxPage.js.map +1 -1
- package/dist/brainyrich/pages/_shared.d.ts +0 -1
- package/dist/brainyrich/pages/_shared.js.map +1 -1
- package/dist/brainyrich/providers/BrainyRichProvider.d.ts +0 -1
- package/dist/brainyrich/providers/BrainyRichProvider.js.map +1 -1
- package/dist/brainyrich/services/createSandboxAssistantTransport.js.map +1 -1
- package/dist/brainyrich/types/bra.d.ts +15 -0
- package/dist/brainyrich/types/bra.d.ts.map +1 -1
- package/dist/brainyrich/types/index.d.ts +0 -1
- package/dist/brainyrich/utils/cn.d.ts +0 -1
- package/dist/brainyrich/utils/i18n.d.ts +0 -1
- package/dist/brainyrich/utils/i18n.js.map +1 -1
- package/dist/brainyrich/utils/nativeControls.d.ts +0 -1
- package/dist/brainyrich/utils/nativeControls.js.map +1 -1
- package/dist/brainyrich/utils/navigation.js.map +1 -1
- package/dist/brainyrich/utils/pageContext.js.map +1 -1
- package/dist/dev/main.d.ts +0 -1
- package/dist/node_modules/clsx/dist/clsx.js.map +1 -1
- package/dist/node_modules/d3-array/src/ascending.js.map +1 -1
- package/dist/node_modules/d3-array/src/bisect.js.map +1 -1
- package/dist/node_modules/d3-array/src/bisector.js.map +1 -1
- package/dist/node_modules/d3-array/src/descending.js.map +1 -1
- package/dist/node_modules/d3-array/src/max.js.map +1 -1
- package/dist/node_modules/d3-array/src/min.js.map +1 -1
- package/dist/node_modules/d3-array/src/number.js.map +1 -1
- package/dist/node_modules/d3-array/src/quantile.js.map +1 -1
- package/dist/node_modules/d3-array/src/quickselect.js.map +1 -1
- package/dist/node_modules/d3-array/src/range.js.map +1 -1
- package/dist/node_modules/d3-array/src/sort.js.map +1 -1
- package/dist/node_modules/d3-array/src/ticks.js.map +1 -1
- package/dist/node_modules/d3-color/src/color.js.map +1 -1
- package/dist/node_modules/d3-color/src/define.js.map +1 -1
- package/dist/node_modules/d3-format/src/defaultLocale.js.map +1 -1
- package/dist/node_modules/d3-format/src/exponent.js.map +1 -1
- package/dist/node_modules/d3-format/src/formatDecimal.js.map +1 -1
- package/dist/node_modules/d3-format/src/formatGroup.js.map +1 -1
- package/dist/node_modules/d3-format/src/formatNumerals.js.map +1 -1
- package/dist/node_modules/d3-format/src/formatPrefixAuto.js.map +1 -1
- package/dist/node_modules/d3-format/src/formatRounded.js.map +1 -1
- package/dist/node_modules/d3-format/src/formatSpecifier.js.map +1 -1
- package/dist/node_modules/d3-format/src/formatTrim.js.map +1 -1
- package/dist/node_modules/d3-format/src/formatTypes.js.map +1 -1
- package/dist/node_modules/d3-format/src/identity.js.map +1 -1
- package/dist/node_modules/d3-format/src/locale.js.map +1 -1
- package/dist/node_modules/d3-format/src/precisionFixed.js.map +1 -1
- package/dist/node_modules/d3-format/src/precisionPrefix.js.map +1 -1
- package/dist/node_modules/d3-format/src/precisionRound.js.map +1 -1
- package/dist/node_modules/d3-interpolate/src/array.js.map +1 -1
- package/dist/node_modules/d3-interpolate/src/basis.js.map +1 -1
- package/dist/node_modules/d3-interpolate/src/basisClosed.js.map +1 -1
- package/dist/node_modules/d3-interpolate/src/color.js.map +1 -1
- package/dist/node_modules/d3-interpolate/src/date.js.map +1 -1
- package/dist/node_modules/d3-interpolate/src/number.js.map +1 -1
- package/dist/node_modules/d3-interpolate/src/numberArray.js.map +1 -1
- package/dist/node_modules/d3-interpolate/src/object.js.map +1 -1
- package/dist/node_modules/d3-interpolate/src/piecewise.js.map +1 -1
- package/dist/node_modules/d3-interpolate/src/rgb.js.map +1 -1
- package/dist/node_modules/d3-interpolate/src/round.js.map +1 -1
- package/dist/node_modules/d3-interpolate/src/string.js.map +1 -1
- package/dist/node_modules/d3-interpolate/src/value.js.map +1 -1
- package/dist/node_modules/d3-path/src/path.js.map +1 -1
- package/dist/node_modules/d3-scale/src/band.js.map +1 -1
- package/dist/node_modules/d3-scale/src/constant.js.map +1 -1
- package/dist/node_modules/d3-scale/src/continuous.js.map +1 -1
- package/dist/node_modules/d3-scale/src/diverging.js.map +1 -1
- package/dist/node_modules/d3-scale/src/identity.js.map +1 -1
- package/dist/node_modules/d3-scale/src/init.js.map +1 -1
- package/dist/node_modules/d3-scale/src/linear.js.map +1 -1
- package/dist/node_modules/d3-scale/src/log.js.map +1 -1
- package/dist/node_modules/d3-scale/src/nice.js.map +1 -1
- package/dist/node_modules/d3-scale/src/number.js.map +1 -1
- package/dist/node_modules/d3-scale/src/ordinal.js.map +1 -1
- package/dist/node_modules/d3-scale/src/pow.js.map +1 -1
- package/dist/node_modules/d3-scale/src/quantile.js.map +1 -1
- package/dist/node_modules/d3-scale/src/quantize.js.map +1 -1
- package/dist/node_modules/d3-scale/src/radial.js.map +1 -1
- package/dist/node_modules/d3-scale/src/sequential.js.map +1 -1
- package/dist/node_modules/d3-scale/src/sequentialQuantile.js.map +1 -1
- package/dist/node_modules/d3-scale/src/symlog.js.map +1 -1
- package/dist/node_modules/d3-scale/src/threshold.js.map +1 -1
- package/dist/node_modules/d3-scale/src/tickFormat.js.map +1 -1
- package/dist/node_modules/d3-scale/src/time.js.map +1 -1
- package/dist/node_modules/d3-scale/src/utcTime.js.map +1 -1
- package/dist/node_modules/d3-shape/src/area.js.map +1 -1
- package/dist/node_modules/d3-shape/src/array.js.map +1 -1
- package/dist/node_modules/d3-shape/src/constant.js.map +1 -1
- package/dist/node_modules/d3-shape/src/curve/basis.js.map +1 -1
- package/dist/node_modules/d3-shape/src/curve/basisClosed.js.map +1 -1
- package/dist/node_modules/d3-shape/src/curve/basisOpen.js.map +1 -1
- package/dist/node_modules/d3-shape/src/curve/bump.js.map +1 -1
- package/dist/node_modules/d3-shape/src/curve/linear.js.map +1 -1
- package/dist/node_modules/d3-shape/src/curve/linearClosed.js.map +1 -1
- package/dist/node_modules/d3-shape/src/curve/monotone.js.map +1 -1
- package/dist/node_modules/d3-shape/src/curve/natural.js.map +1 -1
- package/dist/node_modules/d3-shape/src/curve/step.js.map +1 -1
- package/dist/node_modules/d3-shape/src/line.js.map +1 -1
- package/dist/node_modules/d3-shape/src/noop.js.map +1 -1
- package/dist/node_modules/d3-shape/src/offset/expand.js.map +1 -1
- package/dist/node_modules/d3-shape/src/offset/none.js.map +1 -1
- package/dist/node_modules/d3-shape/src/offset/silhouette.js.map +1 -1
- package/dist/node_modules/d3-shape/src/offset/wiggle.js.map +1 -1
- package/dist/node_modules/d3-shape/src/order/none.js.map +1 -1
- package/dist/node_modules/d3-shape/src/path.js.map +1 -1
- package/dist/node_modules/d3-shape/src/point.js.map +1 -1
- package/dist/node_modules/d3-shape/src/stack.js.map +1 -1
- package/dist/node_modules/d3-shape/src/symbol/circle.js.map +1 -1
- package/dist/node_modules/d3-shape/src/symbol/cross.js.map +1 -1
- package/dist/node_modules/d3-shape/src/symbol/diamond.js.map +1 -1
- package/dist/node_modules/d3-shape/src/symbol/square.js.map +1 -1
- package/dist/node_modules/d3-shape/src/symbol/star.js.map +1 -1
- package/dist/node_modules/d3-shape/src/symbol/triangle.js.map +1 -1
- package/dist/node_modules/d3-shape/src/symbol/wye.js.map +1 -1
- package/dist/node_modules/d3-shape/src/symbol.js.map +1 -1
- package/dist/node_modules/d3-time/src/day.js.map +1 -1
- package/dist/node_modules/d3-time/src/hour.js.map +1 -1
- package/dist/node_modules/d3-time/src/interval.js.map +1 -1
- package/dist/node_modules/d3-time/src/millisecond.js.map +1 -1
- package/dist/node_modules/d3-time/src/minute.js.map +1 -1
- package/dist/node_modules/d3-time/src/month.js.map +1 -1
- package/dist/node_modules/d3-time/src/second.js.map +1 -1
- package/dist/node_modules/d3-time/src/ticks.js.map +1 -1
- package/dist/node_modules/d3-time/src/week.js.map +1 -1
- package/dist/node_modules/d3-time/src/year.js.map +1 -1
- package/dist/node_modules/d3-time-format/src/defaultLocale.js.map +1 -1
- package/dist/node_modules/d3-time-format/src/locale.js.map +1 -1
- package/dist/node_modules/decimal.js-light/decimal.js.map +1 -1
- package/dist/node_modules/eventemitter3/index.js.map +1 -1
- package/dist/node_modules/fast-equals/dist/es/index.js.map +1 -1
- package/dist/node_modules/internmap/src/index.js.map +1 -1
- package/dist/node_modules/lodash/_DataView.js.map +1 -1
- package/dist/node_modules/lodash/_Hash.js.map +1 -1
- package/dist/node_modules/lodash/_ListCache.js.map +1 -1
- package/dist/node_modules/lodash/_Map.js.map +1 -1
- package/dist/node_modules/lodash/_MapCache.js.map +1 -1
- package/dist/node_modules/lodash/_Promise.js.map +1 -1
- package/dist/node_modules/lodash/_Set.js.map +1 -1
- package/dist/node_modules/lodash/_SetCache.js.map +1 -1
- package/dist/node_modules/lodash/_Stack.js +2 -1
- package/dist/node_modules/lodash/_Stack.js.map +1 -1
- package/dist/node_modules/lodash/_Symbol.js.map +1 -1
- package/dist/node_modules/lodash/_Uint8Array.js.map +1 -1
- package/dist/node_modules/lodash/_WeakMap.js.map +1 -1
- package/dist/node_modules/lodash/_apply.js.map +1 -1
- package/dist/node_modules/lodash/_arrayEvery.js.map +1 -1
- package/dist/node_modules/lodash/_arrayFilter.js.map +1 -1
- package/dist/node_modules/lodash/_arrayIncludes.js.map +1 -1
- package/dist/node_modules/lodash/_arrayIncludesWith.js.map +1 -1
- package/dist/node_modules/lodash/_arrayLikeKeys.js.map +1 -1
- package/dist/node_modules/lodash/_arrayMap.js.map +1 -1
- package/dist/node_modules/lodash/_arrayPush.js.map +1 -1
- package/dist/node_modules/lodash/_arraySome.js.map +1 -1
- package/dist/node_modules/lodash/_asciiToArray.js.map +1 -1
- package/dist/node_modules/lodash/_assocIndexOf.js.map +1 -1
- package/dist/node_modules/lodash/_baseAssignValue.js.map +1 -1
- package/dist/node_modules/lodash/_baseEach.js.map +1 -1
- package/dist/node_modules/lodash/_baseEvery.js.map +1 -1
- package/dist/node_modules/lodash/_baseExtremum.js.map +1 -1
- package/dist/node_modules/lodash/_baseFindIndex.js.map +1 -1
- package/dist/node_modules/lodash/_baseFlatten.js.map +1 -1
- package/dist/node_modules/lodash/_baseFor.js.map +1 -1
- package/dist/node_modules/lodash/_baseForOwn.js.map +1 -1
- package/dist/node_modules/lodash/_baseGet.js.map +1 -1
- package/dist/node_modules/lodash/_baseGetAllKeys.js.map +1 -1
- package/dist/node_modules/lodash/_baseGetTag.js.map +1 -1
- package/dist/node_modules/lodash/_baseGt.js.map +1 -1
- package/dist/node_modules/lodash/_baseHasIn.js.map +1 -1
- package/dist/node_modules/lodash/_baseIndexOf.js.map +1 -1
- package/dist/node_modules/lodash/_baseIsArguments.js.map +1 -1
- package/dist/node_modules/lodash/_baseIsEqual.js.map +1 -1
- package/dist/node_modules/lodash/_baseIsEqualDeep.js.map +1 -1
- package/dist/node_modules/lodash/_baseIsMatch.js.map +1 -1
- package/dist/node_modules/lodash/_baseIsNaN.js.map +1 -1
- package/dist/node_modules/lodash/_baseIsNative.js.map +1 -1
- package/dist/node_modules/lodash/_baseIsTypedArray.js.map +1 -1
- package/dist/node_modules/lodash/_baseIteratee.js.map +1 -1
- package/dist/node_modules/lodash/_baseKeys.js.map +1 -1
- package/dist/node_modules/lodash/_baseLt.js.map +1 -1
- package/dist/node_modules/lodash/_baseMap.js.map +1 -1
- package/dist/node_modules/lodash/_baseMatches.js.map +1 -1
- package/dist/node_modules/lodash/_baseMatchesProperty.js.map +1 -1
- package/dist/node_modules/lodash/_baseOrderBy.js.map +1 -1
- package/dist/node_modules/lodash/_baseProperty.js.map +1 -1
- package/dist/node_modules/lodash/_basePropertyDeep.js.map +1 -1
- package/dist/node_modules/lodash/_baseRange.js.map +1 -1
- package/dist/node_modules/lodash/_baseRest.js.map +1 -1
- package/dist/node_modules/lodash/_baseSetToString.js.map +1 -1
- package/dist/node_modules/lodash/_baseSlice.js.map +1 -1
- package/dist/node_modules/lodash/_baseSome.js.map +1 -1
- package/dist/node_modules/lodash/_baseSortBy.js.map +1 -1
- package/dist/node_modules/lodash/_baseTimes.js.map +1 -1
- package/dist/node_modules/lodash/_baseToString.js.map +1 -1
- package/dist/node_modules/lodash/_baseTrim.js.map +1 -1
- package/dist/node_modules/lodash/_baseUnary.js.map +1 -1
- package/dist/node_modules/lodash/_baseUniq.js.map +1 -1
- package/dist/node_modules/lodash/_cacheHas.js.map +1 -1
- package/dist/node_modules/lodash/_castPath.js.map +1 -1
- package/dist/node_modules/lodash/_castSlice.js.map +1 -1
- package/dist/node_modules/lodash/_compareAscending.js.map +1 -1
- package/dist/node_modules/lodash/_compareMultiple.js.map +1 -1
- package/dist/node_modules/lodash/_coreJsData.js.map +1 -1
- package/dist/node_modules/lodash/_createBaseEach.js.map +1 -1
- package/dist/node_modules/lodash/_createBaseFor.js.map +1 -1
- package/dist/node_modules/lodash/_createCaseFirst.js.map +1 -1
- package/dist/node_modules/lodash/_createFind.js.map +1 -1
- package/dist/node_modules/lodash/_createRange.js.map +1 -1
- package/dist/node_modules/lodash/_createSet.js.map +1 -1
- package/dist/node_modules/lodash/_defineProperty.js.map +1 -1
- package/dist/node_modules/lodash/_equalArrays.js.map +1 -1
- package/dist/node_modules/lodash/_equalByTag.js.map +1 -1
- package/dist/node_modules/lodash/_equalObjects.js.map +1 -1
- package/dist/node_modules/lodash/_freeGlobal.js.map +1 -1
- package/dist/node_modules/lodash/_getAllKeys.js.map +1 -1
- package/dist/node_modules/lodash/_getMapData.js.map +1 -1
- package/dist/node_modules/lodash/_getMatchData.js.map +1 -1
- package/dist/node_modules/lodash/_getNative.js.map +1 -1
- package/dist/node_modules/lodash/_getPrototype.js.map +1 -1
- package/dist/node_modules/lodash/_getRawTag.js.map +1 -1
- package/dist/node_modules/lodash/_getSymbols.js.map +1 -1
- package/dist/node_modules/lodash/_getTag.js.map +1 -1
- package/dist/node_modules/lodash/_getValue.js.map +1 -1
- package/dist/node_modules/lodash/_hasPath.js.map +1 -1
- package/dist/node_modules/lodash/_hasUnicode.js.map +1 -1
- package/dist/node_modules/lodash/_hashClear.js.map +1 -1
- package/dist/node_modules/lodash/_hashDelete.js.map +1 -1
- package/dist/node_modules/lodash/_hashGet.js.map +1 -1
- package/dist/node_modules/lodash/_hashHas.js.map +1 -1
- package/dist/node_modules/lodash/_hashSet.js.map +1 -1
- package/dist/node_modules/lodash/_isFlattenable.js.map +1 -1
- package/dist/node_modules/lodash/_isIndex.js.map +1 -1
- package/dist/node_modules/lodash/_isIterateeCall.js.map +1 -1
- package/dist/node_modules/lodash/_isKey.js.map +1 -1
- package/dist/node_modules/lodash/_isKeyable.js.map +1 -1
- package/dist/node_modules/lodash/_isMasked.js.map +1 -1
- package/dist/node_modules/lodash/_isPrototype.js.map +1 -1
- package/dist/node_modules/lodash/_isStrictComparable.js.map +1 -1
- package/dist/node_modules/lodash/_listCacheClear.js.map +1 -1
- package/dist/node_modules/lodash/_listCacheDelete.js.map +1 -1
- package/dist/node_modules/lodash/_listCacheGet.js.map +1 -1
- package/dist/node_modules/lodash/_listCacheHas.js.map +1 -1
- package/dist/node_modules/lodash/_listCacheSet.js.map +1 -1
- package/dist/node_modules/lodash/_mapCacheClear.js.map +1 -1
- package/dist/node_modules/lodash/_mapCacheDelete.js.map +1 -1
- package/dist/node_modules/lodash/_mapCacheGet.js.map +1 -1
- package/dist/node_modules/lodash/_mapCacheHas.js.map +1 -1
- package/dist/node_modules/lodash/_mapCacheSet.js.map +1 -1
- package/dist/node_modules/lodash/_mapToArray.js.map +1 -1
- package/dist/node_modules/lodash/_matchesStrictComparable.js.map +1 -1
- package/dist/node_modules/lodash/_memoizeCapped.js.map +1 -1
- package/dist/node_modules/lodash/_nativeCreate.js.map +1 -1
- package/dist/node_modules/lodash/_nativeKeys.js.map +1 -1
- package/dist/node_modules/lodash/_nodeUtil.js.map +1 -1
- package/dist/node_modules/lodash/_objectToString.js.map +1 -1
- package/dist/node_modules/lodash/_overArg.js.map +1 -1
- package/dist/node_modules/lodash/_overRest.js.map +1 -1
- package/dist/node_modules/lodash/_root.js.map +1 -1
- package/dist/node_modules/lodash/_setCacheAdd.js.map +1 -1
- package/dist/node_modules/lodash/_setCacheHas.js.map +1 -1
- package/dist/node_modules/lodash/_setToArray.js.map +1 -1
- package/dist/node_modules/lodash/_setToString.js.map +1 -1
- package/dist/node_modules/lodash/_shortOut.js.map +1 -1
- package/dist/node_modules/lodash/_stackClear.js.map +1 -1
- package/dist/node_modules/lodash/_stackDelete.js.map +1 -1
- package/dist/node_modules/lodash/_stackGet.js.map +1 -1
- package/dist/node_modules/lodash/_stackHas.js.map +1 -1
- package/dist/node_modules/lodash/_stackSet.js.map +1 -1
- package/dist/node_modules/lodash/_strictIndexOf.js.map +1 -1
- package/dist/node_modules/lodash/_stringToArray.js.map +1 -1
- package/dist/node_modules/lodash/_stringToPath.js.map +1 -1
- package/dist/node_modules/lodash/_toKey.js.map +1 -1
- package/dist/node_modules/lodash/_toSource.js.map +1 -1
- package/dist/node_modules/lodash/_trimmedEndIndex.js.map +1 -1
- package/dist/node_modules/lodash/_unicodeToArray.js.map +1 -1
- package/dist/node_modules/lodash/constant.js.map +1 -1
- package/dist/node_modules/lodash/debounce.js.map +1 -1
- package/dist/node_modules/lodash/eq.js.map +1 -1
- package/dist/node_modules/lodash/every.js.map +1 -1
- package/dist/node_modules/lodash/find.js.map +1 -1
- package/dist/node_modules/lodash/findIndex.js.map +1 -1
- package/dist/node_modules/lodash/flatMap.js.map +1 -1
- package/dist/node_modules/lodash/get.js.map +1 -1
- package/dist/node_modules/lodash/hasIn.js.map +1 -1
- package/dist/node_modules/lodash/identity.js.map +1 -1
- package/dist/node_modules/lodash/isArguments.js.map +1 -1
- package/dist/node_modules/lodash/isArray.js.map +1 -1
- package/dist/node_modules/lodash/isArrayLike.js.map +1 -1
- package/dist/node_modules/lodash/isBoolean.js.map +1 -1
- package/dist/node_modules/lodash/isBuffer.js.map +1 -1
- package/dist/node_modules/lodash/isEqual.js.map +1 -1
- package/dist/node_modules/lodash/isFunction.js.map +1 -1
- package/dist/node_modules/lodash/isLength.js.map +1 -1
- package/dist/node_modules/lodash/isNaN.js.map +1 -1
- package/dist/node_modules/lodash/isNil.js.map +1 -1
- package/dist/node_modules/lodash/isNumber.js.map +1 -1
- package/dist/node_modules/lodash/isObject.js.map +1 -1
- package/dist/node_modules/lodash/isObjectLike.js.map +1 -1
- package/dist/node_modules/lodash/isPlainObject.js.map +1 -1
- package/dist/node_modules/lodash/isString.js.map +1 -1
- package/dist/node_modules/lodash/isSymbol.js.map +1 -1
- package/dist/node_modules/lodash/isTypedArray.js.map +1 -1
- package/dist/node_modules/lodash/keys.js.map +1 -1
- package/dist/node_modules/lodash/last.js.map +1 -1
- package/dist/node_modules/lodash/map.js.map +1 -1
- package/dist/node_modules/lodash/mapValues.js.map +1 -1
- package/dist/node_modules/lodash/max.js.map +1 -1
- package/dist/node_modules/lodash/maxBy.js.map +1 -1
- package/dist/node_modules/lodash/memoize.js.map +1 -1
- package/dist/node_modules/lodash/min.js.map +1 -1
- package/dist/node_modules/lodash/minBy.js.map +1 -1
- package/dist/node_modules/lodash/noop.js.map +1 -1
- package/dist/node_modules/lodash/now.js.map +1 -1
- package/dist/node_modules/lodash/property.js.map +1 -1
- package/dist/node_modules/lodash/range.js.map +1 -1
- package/dist/node_modules/lodash/some.js.map +1 -1
- package/dist/node_modules/lodash/sortBy.js.map +1 -1
- package/dist/node_modules/lodash/stubArray.js.map +1 -1
- package/dist/node_modules/lodash/stubFalse.js.map +1 -1
- package/dist/node_modules/lodash/throttle.js.map +1 -1
- package/dist/node_modules/lodash/toFinite.js.map +1 -1
- package/dist/node_modules/lodash/toInteger.js.map +1 -1
- package/dist/node_modules/lodash/toNumber.js.map +1 -1
- package/dist/node_modules/lodash/toString.js.map +1 -1
- package/dist/node_modules/lodash/uniqBy.js.map +1 -1
- package/dist/node_modules/lodash/upperFirst.js.map +1 -1
- package/dist/node_modules/object-assign/index.js.map +1 -1
- package/dist/node_modules/prop-types/checkPropTypes.js.map +1 -1
- package/dist/node_modules/prop-types/factoryWithThrowingShims.js.map +1 -1
- package/dist/node_modules/prop-types/factoryWithTypeCheckers.js.map +1 -1
- package/dist/node_modules/prop-types/index.js.map +1 -1
- package/dist/node_modules/prop-types/lib/ReactPropTypesSecret.js.map +1 -1
- package/dist/node_modules/prop-types/lib/has.js.map +1 -1
- package/dist/node_modules/prop-types/node_modules/react-is/cjs/react-is.development.js.map +1 -1
- package/dist/node_modules/prop-types/node_modules/react-is/cjs/react-is.production.min.js.map +1 -1
- package/dist/node_modules/prop-types/node_modules/react-is/index.js.map +1 -1
- package/dist/node_modules/react-is/cjs/react-is.development.js.map +1 -1
- package/dist/node_modules/react-is/cjs/react-is.production.min.js.map +1 -1
- package/dist/node_modules/react-is/index.js.map +1 -1
- package/dist/node_modules/react-smooth/es6/Animate.js +2 -2
- package/dist/node_modules/react-smooth/es6/Animate.js.map +1 -1
- package/dist/node_modules/react-smooth/es6/AnimateManager.js.map +1 -1
- package/dist/node_modules/react-smooth/es6/configUpdate.js.map +1 -1
- package/dist/node_modules/react-smooth/es6/easing.js.map +1 -1
- package/dist/node_modules/react-smooth/es6/setRafTimeout.js.map +1 -1
- package/dist/node_modules/react-smooth/es6/util.js.map +1 -1
- package/dist/node_modules/recharts/es6/cartesian/Area.js +13 -13
- package/dist/node_modules/recharts/es6/cartesian/Area.js.map +1 -1
- package/dist/node_modules/recharts/es6/cartesian/Bar.js +9 -9
- package/dist/node_modules/recharts/es6/cartesian/Bar.js.map +1 -1
- package/dist/node_modules/recharts/es6/cartesian/Brush.js +12 -12
- package/dist/node_modules/recharts/es6/cartesian/Brush.js.map +1 -1
- package/dist/node_modules/recharts/es6/cartesian/CartesianAxis.js +7 -7
- package/dist/node_modules/recharts/es6/cartesian/CartesianAxis.js.map +1 -1
- package/dist/node_modules/recharts/es6/cartesian/ErrorBar.js +4 -4
- package/dist/node_modules/recharts/es6/cartesian/ErrorBar.js.map +1 -1
- package/dist/node_modules/recharts/es6/cartesian/ReferenceArea.js +3 -3
- package/dist/node_modules/recharts/es6/cartesian/ReferenceArea.js.map +1 -1
- package/dist/node_modules/recharts/es6/cartesian/ReferenceDot.js +3 -3
- package/dist/node_modules/recharts/es6/cartesian/ReferenceDot.js.map +1 -1
- package/dist/node_modules/recharts/es6/cartesian/ReferenceLine.js +4 -4
- package/dist/node_modules/recharts/es6/cartesian/ReferenceLine.js.map +1 -1
- package/dist/node_modules/recharts/es6/cartesian/XAxis.js +3 -3
- package/dist/node_modules/recharts/es6/cartesian/XAxis.js.map +1 -1
- package/dist/node_modules/recharts/es6/cartesian/YAxis.js +3 -3
- package/dist/node_modules/recharts/es6/cartesian/YAxis.js.map +1 -1
- package/dist/node_modules/recharts/es6/cartesian/getEquidistantTicks.js.map +1 -1
- package/dist/node_modules/recharts/es6/cartesian/getTicks.js.map +1 -1
- package/dist/node_modules/recharts/es6/chart/AccessibilityManager.js +1 -1
- package/dist/node_modules/recharts/es6/chart/AccessibilityManager.js.map +1 -1
- package/dist/node_modules/recharts/es6/chart/AreaChart.js.map +1 -1
- package/dist/node_modules/recharts/es6/chart/BarChart.js.map +1 -1
- package/dist/node_modules/recharts/es6/chart/PieChart.js.map +1 -1
- package/dist/node_modules/recharts/es6/chart/generateCategoricalChart.js +22 -22
- package/dist/node_modules/recharts/es6/chart/generateCategoricalChart.js.map +1 -1
- package/dist/node_modules/recharts/es6/component/Cell.js.map +1 -1
- package/dist/node_modules/recharts/es6/component/Cursor.js +1 -1
- package/dist/node_modules/recharts/es6/component/Cursor.js.map +1 -1
- package/dist/node_modules/recharts/es6/component/DefaultLegendContent.js +11 -11
- package/dist/node_modules/recharts/es6/component/DefaultLegendContent.js.map +1 -1
- package/dist/node_modules/recharts/es6/component/DefaultTooltipContent.js +6 -6
- package/dist/node_modules/recharts/es6/component/DefaultTooltipContent.js.map +1 -1
- package/dist/node_modules/recharts/es6/component/Label.js +14 -14
- package/dist/node_modules/recharts/es6/component/Label.js.map +1 -1
- package/dist/node_modules/recharts/es6/component/LabelList.js +6 -6
- package/dist/node_modules/recharts/es6/component/LabelList.js.map +1 -1
- package/dist/node_modules/recharts/es6/component/Legend.js +5 -5
- package/dist/node_modules/recharts/es6/component/Legend.js.map +1 -1
- package/dist/node_modules/recharts/es6/component/ResponsiveContainer.js +3 -3
- package/dist/node_modules/recharts/es6/component/ResponsiveContainer.js.map +1 -1
- package/dist/node_modules/recharts/es6/component/Text.js +2 -2
- package/dist/node_modules/recharts/es6/component/Text.js.map +1 -1
- package/dist/node_modules/recharts/es6/component/Tooltip.js +3 -3
- package/dist/node_modules/recharts/es6/component/Tooltip.js.map +1 -1
- package/dist/node_modules/recharts/es6/component/TooltipBoundingBox.js +2 -2
- package/dist/node_modules/recharts/es6/component/TooltipBoundingBox.js.map +1 -1
- package/dist/node_modules/recharts/es6/container/Layer.js +2 -2
- package/dist/node_modules/recharts/es6/container/Layer.js.map +1 -1
- package/dist/node_modules/recharts/es6/container/Surface.js +2 -2
- package/dist/node_modules/recharts/es6/container/Surface.js.map +1 -1
- package/dist/node_modules/recharts/es6/context/chartLayoutContext.js +2 -2
- package/dist/node_modules/recharts/es6/context/chartLayoutContext.js.map +1 -1
- package/dist/node_modules/recharts/es6/polar/Pie.js +13 -13
- package/dist/node_modules/recharts/es6/polar/Pie.js.map +1 -1
- package/dist/node_modules/recharts/es6/polar/PolarAngleAxis.js +8 -8
- package/dist/node_modules/recharts/es6/polar/PolarAngleAxis.js.map +1 -1
- package/dist/node_modules/recharts/es6/polar/PolarRadiusAxis.js +6 -6
- package/dist/node_modules/recharts/es6/polar/PolarRadiusAxis.js.map +1 -1
- package/dist/node_modules/recharts/es6/shape/Cross.js +1 -1
- package/dist/node_modules/recharts/es6/shape/Cross.js.map +1 -1
- package/dist/node_modules/recharts/es6/shape/Curve.js +1 -1
- package/dist/node_modules/recharts/es6/shape/Curve.js.map +1 -1
- package/dist/node_modules/recharts/es6/shape/Dot.js +1 -1
- package/dist/node_modules/recharts/es6/shape/Dot.js.map +1 -1
- package/dist/node_modules/recharts/es6/shape/Polygon.js +4 -4
- package/dist/node_modules/recharts/es6/shape/Polygon.js.map +1 -1
- package/dist/node_modules/recharts/es6/shape/Rectangle.js +4 -4
- package/dist/node_modules/recharts/es6/shape/Rectangle.js.map +1 -1
- package/dist/node_modules/recharts/es6/shape/Sector.js +1 -1
- package/dist/node_modules/recharts/es6/shape/Sector.js.map +1 -1
- package/dist/node_modules/recharts/es6/shape/Symbols.js +1 -1
- package/dist/node_modules/recharts/es6/shape/Symbols.js.map +1 -1
- package/dist/node_modules/recharts/es6/shape/Trapezoid.js +4 -4
- package/dist/node_modules/recharts/es6/shape/Trapezoid.js.map +1 -1
- package/dist/node_modules/recharts/es6/util/ActiveShapeUtils.js +9 -9
- package/dist/node_modules/recharts/es6/util/ActiveShapeUtils.js.map +1 -1
- package/dist/node_modules/recharts/es6/util/BarUtils.js +1 -1
- package/dist/node_modules/recharts/es6/util/BarUtils.js.map +1 -1
- package/dist/node_modules/recharts/es6/util/CartesianUtils.js +1 -1
- package/dist/node_modules/recharts/es6/util/CartesianUtils.js.map +1 -1
- package/dist/node_modules/recharts/es6/util/ChartUtils.js.map +1 -1
- package/dist/node_modules/recharts/es6/util/CssPrefixUtils.js.map +1 -1
- package/dist/node_modules/recharts/es6/util/DOMUtils.js.map +1 -1
- package/dist/node_modules/recharts/es6/util/DataUtils.js.map +1 -1
- package/dist/node_modules/recharts/es6/util/DetectReferenceElementsDomain.js.map +1 -1
- package/dist/node_modules/recharts/es6/util/Events.js.map +1 -1
- package/dist/node_modules/recharts/es6/util/Global.js.map +1 -1
- package/dist/node_modules/recharts/es6/util/IfOverflowMatches.js.map +1 -1
- package/dist/node_modules/recharts/es6/util/LogUtils.js.map +1 -1
- package/dist/node_modules/recharts/es6/util/PolarUtils.js +1 -1
- package/dist/node_modules/recharts/es6/util/PolarUtils.js.map +1 -1
- package/dist/node_modules/recharts/es6/util/ReactUtils.js +1 -1
- package/dist/node_modules/recharts/es6/util/ReactUtils.js.map +1 -1
- package/dist/node_modules/recharts/es6/util/ReduceCSSCalc.js +1 -1
- package/dist/node_modules/recharts/es6/util/ReduceCSSCalc.js.map +1 -1
- package/dist/node_modules/recharts/es6/util/ShallowEqual.js.map +1 -1
- package/dist/node_modules/recharts/es6/util/TickUtils.js.map +1 -1
- package/dist/node_modules/recharts/es6/util/calculateViewBox.js.map +1 -1
- package/dist/node_modules/recharts/es6/util/cursor/getCursorPoints.js.map +1 -1
- package/dist/node_modules/recharts/es6/util/cursor/getCursorRectangle.js.map +1 -1
- package/dist/node_modules/recharts/es6/util/cursor/getRadialCursorPoints.js.map +1 -1
- package/dist/node_modules/recharts/es6/util/getEveryNthWithCondition.js.map +1 -1
- package/dist/node_modules/recharts/es6/util/getLegendProps.js.map +1 -1
- package/dist/node_modules/recharts/es6/util/isDomainSpecifiedByUser.js.map +1 -1
- package/dist/node_modules/recharts/es6/util/payload/getUniqPayload.js.map +1 -1
- package/dist/node_modules/recharts/es6/util/tooltip/translate.js.map +1 -1
- package/dist/node_modules/recharts/es6/util/types.js +1 -1
- package/dist/node_modules/recharts/es6/util/types.js.map +1 -1
- package/dist/node_modules/recharts-scale/es6/getNiceTickValues.js.map +1 -1
- package/dist/node_modules/recharts-scale/es6/util/arithmetic.js.map +1 -1
- package/dist/node_modules/recharts-scale/es6/util/utils.js.map +1 -1
- package/dist/node_modules/sonner/dist/index.js.map +1 -1
- package/dist/node_modules/tailwind-merge/dist/bundle-mjs.js +1 -1
- package/dist/node_modules/tailwind-merge/dist/bundle-mjs.js.map +1 -1
- package/dist/node_modules/tiny-invariant/dist/esm/tiny-invariant.js.map +1 -1
- package/dist/stories/LoadMore.stories.d.ts +0 -1
- package/package.json +15 -7
|
@@ -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 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 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PortfoliosPage.js","names":[],"sources":["../../../src/brainyrich/pages/PortfoliosPage.tsx"],"sourcesContent":["import { useMemo, useState } from 'react';\nimport {\n Badge,\n Button,\n Card,\n CardContent,\n CardDescription,\n CardHeader,\n CardTitle,\n Dialog,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuLabel,\n DropdownMenuSeparator,\n DropdownMenuTrigger,\n Input,\n Label,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n Sheet,\n SheetContent,\n SheetDescription,\n SheetHeader,\n SheetTitle,\n Table,\n TableBody,\n TableCell,\n TableHead,\n TableHeader,\n TableRow,\n Textarea,\n} from '@burdenoff/fe-libs/ui';\nimport {\n ArrowDownRight,\n ArrowUpRight,\n Briefcase,\n MoreHorizontal,\n Pencil,\n Plus,\n Trash2,\n} from 'lucide-react';\n\nimport {\n useArchivePortfolio,\n useCreateHolding,\n useCreatePortfolio,\n useDeleteHolding,\n useHouseholds,\n useModelPortfolios,\n usePagedPortfolios,\n usePortfolio,\n useUpdateHolding,\n useUpdatePortfolio,\n} from '../hooks/useBrainyRichApi';\nimport {\n AccountPicker,\n ConfirmDialog,\n DonutChart,\n FadeIn,\n GlassCard,\n LoadMore,\n StatTile,\n type DonutDatum,\n type VizTone,\n} from '../components';\nimport { CommentThread } from '../components/reuse/CommentThread';\nimport { TagField } from '../components/reuse/TagField';\nimport { hapticSuccess, showToast } from '../utils/nativeControls';\nimport { useTrackBrainyRichPageView } from '../hooks/useBrainyRichAnalytics';\nimport type { BraAssetClass, BraHolding, BraPortfolio, BraPortfolioStatus } from '../types/bra';\nimport {\n AllocationBars,\n EmptyState,\n ErrorState,\n LoadingState,\n SectionShell,\n formatCompact,\n formatMoney,\n formatPct,\n statusBadgeVariant,\n type AllocationDatum,\n} from './_shared';\n\nconst ASSET_CLASSES: BraAssetClass[] = [\n 'EQUITY',\n 'FIXED_INCOME',\n 'CASH',\n 'REAL_ESTATE',\n 'COMMODITY',\n 'CRYPTO',\n 'ALTERNATIVE',\n];\n\nconst PORTFOLIO_STATUSES: BraPortfolioStatus[] = ['DRAFT', 'ACTIVE', 'ARCHIVED'];\n\nconst NONE = '__none__';\n\nconst prettyEnum = (value: string): string =>\n value\n .toLowerCase()\n .split('_')\n .map((w) => w.charAt(0).toUpperCase() + w.slice(1))\n .join(' ');\n\nconst toNumber = (value: string): number | undefined => {\n if (value.trim() === '') return undefined;\n const n = Number(value);\n return Number.isFinite(n) ? n : undefined;\n};\n\n/** Stable, semantic tone per asset class so donut/legend colours read consistently. */\nconst ASSET_TONE: Record<string, 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\nconst toneForClass = (raw: string): VizTone => ASSET_TONE[raw.toUpperCase()] ?? 'neutral';\n\n/** Read a `targetAllocation` JSON blob as an assetClass → percentage map. */\nfunction parseTargetAllocation(raw: unknown): AllocationDatum[] {\n if (!raw || typeof raw !== 'object') return [];\n return Object.entries(raw as Record<string, unknown>)\n .map(([label, value]) => ({ label, value: Number(value) }))\n .filter((d) => Number.isFinite(d.value))\n .map((d) => ({ label: prettyEnum(d.label), value: d.value, tone: 'accent' as const }));\n}\n\n/** Read a `targetAllocation` JSON blob as tone-keyed donut slices (percent values). */\nfunction targetAllocationDonut(raw: unknown): DonutDatum[] {\n if (!raw || typeof raw !== 'object') return [];\n return Object.entries(raw as Record<string, unknown>)\n .map(([key, value]) => ({ key, value: Number(value) }))\n .filter((d) => Number.isFinite(d.value) && d.value > 0)\n .sort((a, b) => b.value - a.value)\n .map((d) => ({ label: prettyEnum(d.key), value: d.value, tone: toneForClass(d.key) }));\n}\n\n// ============================================================================\n// Gain cell — signed money with icon + semantic color (color never sole signal)\n// ============================================================================\n\nfunction GainValue({ value, currency }: { value: number; currency: string }) {\n const positive = value > 0;\n const negative = value < 0;\n const tone = positive\n ? 'text-status-success-text'\n : negative\n ? 'text-status-error-text'\n : 'text-text-secondary';\n const Icon = positive ? ArrowUpRight : negative ? ArrowDownRight : null;\n return (\n <span className={`inline-flex items-center justify-end gap-1 font-medium ${tone}`}>\n {Icon ? <Icon className=\"size-3.5\" aria-hidden=\"true\" /> : null}\n {positive ? '+' : ''}\n {formatMoney(value, currency)}\n </span>\n );\n}\n\n// ============================================================================\n// Portfolio create / edit dialog\n// ============================================================================\n\ninterface PortfolioFormState {\n name: string;\n baseCurrency: string;\n description: string;\n driftThresholdPct: string;\n status: BraPortfolioStatus;\n modelPortfolioId: string;\n householdId: string;\n}\n\nfunction emptyForm(): PortfolioFormState {\n return {\n name: '',\n baseCurrency: 'USD',\n description: '',\n driftThresholdPct: '5',\n status: 'DRAFT',\n modelPortfolioId: NONE,\n householdId: NONE,\n };\n}\n\nfunction PortfolioFormDialog({\n open,\n onOpenChange,\n editing,\n}: {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n editing: BraPortfolio | null;\n}) {\n const { data: models } = useModelPortfolios();\n const { data: households } = useHouseholds();\n const createPortfolio = useCreatePortfolio();\n const updatePortfolio = useUpdatePortfolio();\n\n const [form, setForm] = useState<PortfolioFormState>(emptyForm);\n // Re-seed the form whenever the dialog target changes.\n const [seededFor, setSeededFor] = useState<string | null>(null);\n const targetKey = editing?.id ?? 'new';\n if (open && seededFor !== targetKey) {\n setSeededFor(targetKey);\n setForm(\n editing\n ? {\n name: editing.name,\n baseCurrency: editing.baseCurrency,\n description: editing.description ?? '',\n driftThresholdPct: String(editing.driftThresholdPct),\n status: editing.status,\n modelPortfolioId: editing.modelPortfolioId ?? NONE,\n householdId: editing.householdId ?? NONE,\n }\n : emptyForm()\n );\n }\n if (!open && seededFor !== null) {\n setSeededFor(null);\n }\n\n const pending = createPortfolio.isPending || updatePortfolio.isPending;\n const nameValid = form.name.trim().length > 0;\n\n const handleSubmit = () => {\n if (!nameValid) return;\n const drift = toNumber(form.driftThresholdPct);\n const modelPortfolioId = form.modelPortfolioId === NONE ? null : form.modelPortfolioId;\n const householdId = form.householdId === NONE ? null : form.householdId;\n\n if (editing) {\n updatePortfolio.mutate(\n {\n id: editing.id,\n input: {\n name: form.name.trim(),\n baseCurrency: form.baseCurrency.trim() || 'USD',\n description: form.description.trim() || null,\n driftThresholdPct: drift,\n status: form.status,\n modelPortfolioId,\n },\n },\n {\n onSuccess: () => {\n hapticSuccess();\n void showToast('Portfolio saved');\n onOpenChange(false);\n },\n }\n );\n } else {\n createPortfolio.mutate(\n {\n name: form.name.trim(),\n baseCurrency: form.baseCurrency.trim() || 'USD',\n description: form.description.trim() || null,\n driftThresholdPct: drift,\n modelPortfolioId,\n householdId,\n },\n {\n onSuccess: () => {\n hapticSuccess();\n void showToast('Portfolio created');\n onOpenChange(false);\n },\n }\n );\n }\n };\n\n return (\n <Dialog open={open} onOpenChange={onOpenChange}>\n <DialogContent className=\"max-h-[90vh] overflow-y-auto sm:max-w-lg\">\n <DialogHeader>\n <DialogTitle>{editing ? 'Edit portfolio' : 'New portfolio'}</DialogTitle>\n <DialogDescription>\n {editing\n ? 'Update this portfolio’s strategy settings.'\n : 'Create an investment portfolio to organize and rebalance holdings.'}\n </DialogDescription>\n </DialogHeader>\n\n <div className=\"space-y-4 py-2\">\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"pf-name\">Name</Label>\n <Input\n id=\"pf-name\"\n value={form.name}\n onChange={(e) => setForm((f) => ({ ...f, name: e.target.value }))}\n placeholder=\"Growth Portfolio\"\n aria-invalid={!nameValid}\n />\n </div>\n\n <div className=\"grid grid-cols-2 gap-4\">\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"pf-currency\">Base currency</Label>\n <Input\n id=\"pf-currency\"\n value={form.baseCurrency}\n onChange={(e) =>\n setForm((f) => ({ ...f, baseCurrency: e.target.value.toUpperCase() }))\n }\n placeholder=\"USD\"\n maxLength={3}\n />\n </div>\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"pf-drift\">Drift threshold %</Label>\n <Input\n id=\"pf-drift\"\n type=\"number\"\n min={0}\n step=\"0.5\"\n value={form.driftThresholdPct}\n onChange={(e) => setForm((f) => ({ ...f, driftThresholdPct: e.target.value }))}\n />\n </div>\n </div>\n\n {editing ? (\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"pf-status\">Status</Label>\n <Select\n value={form.status}\n onValueChange={(v) => setForm((f) => ({ ...f, status: v as BraPortfolioStatus }))}\n >\n <SelectTrigger id=\"pf-status\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {PORTFOLIO_STATUSES.map((s) => (\n <SelectItem key={s} value={s}>\n {prettyEnum(s)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n ) : null}\n\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"pf-model\">Model portfolio</Label>\n <Select\n value={form.modelPortfolioId}\n onValueChange={(v) => setForm((f) => ({ ...f, modelPortfolioId: v }))}\n >\n <SelectTrigger id=\"pf-model\">\n <SelectValue placeholder=\"None\" />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value={NONE}>None</SelectItem>\n {models.map((m) => (\n <SelectItem key={m.id} value={m.id}>\n {m.name}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n\n {!editing ? (\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"pf-household\">Household</Label>\n <Select\n value={form.householdId}\n onValueChange={(v) => setForm((f) => ({ ...f, householdId: v }))}\n >\n <SelectTrigger id=\"pf-household\">\n <SelectValue placeholder=\"None\" />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value={NONE}>None</SelectItem>\n {households.map((h) => (\n <SelectItem key={h.id} value={h.id}>\n {h.name}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n ) : null}\n\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"pf-desc\">Description</Label>\n <Textarea\n id=\"pf-desc\"\n value={form.description}\n onChange={(e) => setForm((f) => ({ ...f, description: e.target.value }))}\n placeholder=\"Optional notes about this strategy\"\n rows={3}\n />\n </div>\n </div>\n\n <DialogFooter>\n <Button variant=\"outline\" onClick={() => onOpenChange(false)} disabled={pending}>\n Cancel\n </Button>\n <Button onClick={handleSubmit} disabled={!nameValid || pending}>\n {pending ? 'Saving…' : editing ? 'Save changes' : 'Create portfolio'}\n </Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n );\n}\n\n// ============================================================================\n// Holding create / edit dialog (scoped to a portfolio)\n// ============================================================================\n\ninterface HoldingFormState {\n accountId: string;\n symbol: string;\n name: string;\n assetClass: BraAssetClass;\n quantity: string;\n lastPrice: string;\n costBasis: string;\n}\n\nfunction emptyHoldingForm(): HoldingFormState {\n return {\n accountId: '',\n symbol: '',\n name: '',\n assetClass: 'EQUITY',\n quantity: '0',\n lastPrice: '0',\n costBasis: '0',\n };\n}\n\nfunction HoldingFormDialog({\n open,\n onOpenChange,\n portfolioId,\n editing,\n}: {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n portfolioId: string;\n editing: BraHolding | null;\n}) {\n const createHolding = useCreateHolding();\n const updateHolding = useUpdateHolding();\n\n const [form, setForm] = useState<HoldingFormState>(emptyHoldingForm);\n const [seededFor, setSeededFor] = useState<string | null>(null);\n const targetKey = editing?.id ?? 'new';\n if (open && seededFor !== targetKey) {\n setSeededFor(targetKey);\n setForm(\n editing\n ? {\n accountId: editing.accountId,\n symbol: editing.symbol,\n name: editing.name ?? '',\n assetClass: editing.assetClass,\n quantity: String(editing.quantity),\n lastPrice: String(editing.lastPrice),\n costBasis: String(editing.costBasis),\n }\n : emptyHoldingForm()\n );\n }\n if (!open && seededFor !== null) {\n setSeededFor(null);\n }\n\n const pending = createHolding.isPending || updateHolding.isPending;\n const symbolValid = form.symbol.trim().length > 0;\n const accountValid = editing ? true : form.accountId.trim().length > 0;\n const canSubmit = symbolValid && accountValid && !pending;\n\n const handleSubmit = () => {\n if (!symbolValid || !accountValid) return;\n if (editing) {\n updateHolding.mutate(\n {\n id: editing.id,\n input: {\n name: form.name.trim() || null,\n assetClass: form.assetClass,\n quantity: toNumber(form.quantity),\n lastPrice: toNumber(form.lastPrice),\n costBasis: toNumber(form.costBasis),\n },\n },\n {\n onSuccess: () => {\n hapticSuccess();\n void showToast('Holding saved');\n onOpenChange(false);\n },\n }\n );\n } else {\n createHolding.mutate(\n {\n accountId: form.accountId,\n portfolioId,\n symbol: form.symbol.trim().toUpperCase(),\n name: form.name.trim() || null,\n assetClass: form.assetClass,\n quantity: toNumber(form.quantity),\n lastPrice: toNumber(form.lastPrice),\n costBasis: toNumber(form.costBasis),\n },\n {\n onSuccess: () => {\n hapticSuccess();\n void showToast('Holding added');\n onOpenChange(false);\n },\n }\n );\n }\n };\n\n return (\n <Dialog open={open} onOpenChange={onOpenChange}>\n <DialogContent className=\"max-h-[90vh] overflow-y-auto sm:max-w-lg\">\n <DialogHeader>\n <DialogTitle>{editing ? 'Edit holding' : 'Add holding'}</DialogTitle>\n <DialogDescription>\n {editing\n ? 'Update the position details for this holding.'\n : 'Add a position to this portfolio from one of your accounts.'}\n </DialogDescription>\n </DialogHeader>\n\n <div className=\"space-y-4 py-2\">\n {!editing ? (\n <AccountPicker\n id=\"h-account\"\n value={form.accountId}\n onChange={(v) => setForm((f) => ({ ...f, accountId: v }))}\n placeholder=\"Select account\"\n />\n ) : null}\n\n <div className=\"grid grid-cols-2 gap-4\">\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"h-symbol\">Symbol</Label>\n <Input\n id=\"h-symbol\"\n value={form.symbol}\n onChange={(e) => setForm((f) => ({ ...f, symbol: e.target.value }))}\n placeholder=\"AAPL\"\n disabled={Boolean(editing)}\n aria-invalid={!symbolValid}\n />\n </div>\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"h-class\">Asset class</Label>\n <Select\n value={form.assetClass}\n onValueChange={(v) => setForm((f) => ({ ...f, assetClass: v as BraAssetClass }))}\n >\n <SelectTrigger id=\"h-class\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {ASSET_CLASSES.map((c) => (\n <SelectItem key={c} value={c}>\n {prettyEnum(c)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n </div>\n\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"h-name\">Name</Label>\n <Input\n id=\"h-name\"\n value={form.name}\n onChange={(e) => setForm((f) => ({ ...f, name: e.target.value }))}\n placeholder=\"Apple Inc.\"\n />\n </div>\n\n <div className=\"grid grid-cols-3 gap-4\">\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"h-qty\">Quantity</Label>\n <Input\n id=\"h-qty\"\n type=\"number\"\n step=\"any\"\n value={form.quantity}\n onChange={(e) => setForm((f) => ({ ...f, quantity: e.target.value }))}\n />\n </div>\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"h-price\">Last price</Label>\n <Input\n id=\"h-price\"\n type=\"number\"\n step=\"any\"\n value={form.lastPrice}\n onChange={(e) => setForm((f) => ({ ...f, lastPrice: e.target.value }))}\n />\n </div>\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"h-cost\">Cost basis</Label>\n <Input\n id=\"h-cost\"\n type=\"number\"\n step=\"any\"\n value={form.costBasis}\n onChange={(e) => setForm((f) => ({ ...f, costBasis: e.target.value }))}\n />\n </div>\n </div>\n </div>\n\n <DialogFooter>\n <Button variant=\"outline\" onClick={() => onOpenChange(false)} disabled={pending}>\n Cancel\n </Button>\n <Button onClick={handleSubmit} disabled={!canSubmit}>\n {pending ? 'Saving…' : editing ? 'Save changes' : 'Add holding'}\n </Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n );\n}\n\n// ============================================================================\n// Portfolio detail sheet — holdings table + target vs actual allocation\n// ============================================================================\n\nfunction PortfolioDetailSheet({\n portfolioId,\n onOpenChange,\n}: {\n portfolioId: string | null;\n onOpenChange: (open: boolean) => void;\n}) {\n const { data: portfolio, isLoading, isError, refetch } = usePortfolio(portfolioId ?? undefined);\n const deleteHolding = useDeleteHolding();\n\n const [holdingDialogOpen, setHoldingDialogOpen] = useState(false);\n const [editingHolding, setEditingHolding] = useState<BraHolding | null>(null);\n const [holdingToDelete, setHoldingToDelete] = useState<BraHolding | null>(null);\n\n const holdings = portfolio?.holdings ?? [];\n const currency = portfolio?.baseCurrency ?? 'USD';\n const totalMarketValue = holdings.reduce((s, h) => s + h.marketValue, 0);\n const totalCostBasis = holdings.reduce((s, h) => s + h.costBasis, 0);\n const totalGain = totalMarketValue - totalCostBasis;\n const gainPct = totalCostBasis > 0 ? (totalGain / totalCostBasis) * 100 : 0;\n\n /** Live allocation by class in base-currency value — tone-keyed donut slices. */\n const actualDonut: DonutDatum[] = useMemo(() => {\n if (holdings.length === 0 || totalMarketValue <= 0) return [];\n const byClass = new Map<string, number>();\n holdings.forEach((h) =>\n byClass.set(h.assetClass, (byClass.get(h.assetClass) ?? 0) + h.marketValue)\n );\n return Array.from(byClass.entries())\n .map(([cls, mv]) => ({ label: prettyEnum(cls), value: mv, tone: toneForClass(cls) }))\n .sort((a, b) => b.value - a.value);\n }, [holdings, totalMarketValue]);\n\n /** Actual allocation as percentages, keyed by pretty class label, for drift comparison. */\n const actualPctByLabel = useMemo(() => {\n const map = new Map<string, number>();\n if (totalMarketValue <= 0) return map;\n holdings.forEach((h) =>\n map.set(\n prettyEnum(h.assetClass),\n (map.get(prettyEnum(h.assetClass)) ?? 0) + (h.marketValue / totalMarketValue) * 100\n )\n );\n return map;\n }, [holdings, totalMarketValue]);\n\n /** Target vs actual: each target row captioned with the live actual weight. */\n const targetVsActual: AllocationDatum[] = useMemo(() => {\n return parseTargetAllocation(portfolio?.targetAllocation).map((d) => {\n const actual = actualPctByLabel.get(d.label) ?? 0;\n return {\n label: d.label,\n value: d.value,\n tone: 'accent' as const,\n caption: `${formatPct(d.value, 0)} target · ${formatPct(actual, 0)} actual`,\n };\n });\n }, [portfolio?.targetAllocation, actualPctByLabel]);\n\n const openAddHolding = () => {\n setEditingHolding(null);\n setHoldingDialogOpen(true);\n };\n const openEditHolding = (h: BraHolding) => {\n setEditingHolding(h);\n setHoldingDialogOpen(true);\n };\n\n return (\n <Sheet open={portfolioId !== null} onOpenChange={onOpenChange}>\n <SheetContent className=\"flex w-full flex-col gap-0 overflow-y-auto p-0 sm:max-w-2xl\">\n {isLoading ? (\n <div className=\"p-6\">\n <LoadingState rows={5} />\n </div>\n ) : isError ? (\n <div className=\"p-6\">\n <ErrorState onRetry={() => refetch()} />\n </div>\n ) : !portfolio ? (\n <div className=\"p-6\">\n <EmptyState\n icon={<Briefcase className=\"size-6\" aria-hidden=\"true\" />}\n title=\"Portfolio not found\"\n description=\"This portfolio may have been archived or removed.\"\n />\n </div>\n ) : (\n <>\n <SheetHeader className=\"border-b border-border-subtle p-6\">\n <div className=\"flex items-center gap-2\">\n <SheetTitle className=\"text-xl\">{portfolio.name}</SheetTitle>\n <Badge variant={statusBadgeVariant(portfolio.status)}>\n {prettyEnum(portfolio.status)}\n </Badge>\n </div>\n <SheetDescription>\n {portfolio.description?.trim()\n ? portfolio.description\n : `${portfolio.baseCurrency} · Drift threshold ${formatPct(portfolio.driftThresholdPct)}`}\n </SheetDescription>\n </SheetHeader>\n\n <div className=\"space-y-6 p-6\">\n {/* Hero KPIs — market value carries an emerald glow + live gain delta */}\n <FadeIn className=\"grid grid-cols-2 gap-3 lg:grid-cols-4\">\n <StatTile\n label=\"Market value\"\n value={formatMoney(totalMarketValue, currency)}\n glow\n />\n <StatTile\n label=\"Unrealized gain\"\n value={formatMoney(totalGain, currency)}\n delta={totalCostBasis > 0 ? gainPct : undefined}\n deltaTone={totalGain > 0 ? 'positive' : totalGain < 0 ? 'negative' : 'neutral'}\n />\n <StatTile label=\"Holdings\" value={String(holdings.length)} />\n <StatTile label=\"Drift threshold\" value={formatPct(portfolio.driftThresholdPct)} />\n </FadeIn>\n\n {/* Target vs actual allocation — live donut + drift-aware target bars */}\n <FadeIn index={1}>\n <GlassCard glow className=\"space-y-5\">\n <div className=\"flex items-center justify-between gap-2\">\n <h3 className=\"text-sm font-semibold text-text-primary\">\n Target vs actual allocation\n </h3>\n <Badge variant=\"outline\">{currency}</Badge>\n </div>\n <div className=\"grid gap-6 md:grid-cols-2\">\n <div className=\"space-y-2\">\n <p className=\"text-xs font-medium uppercase tracking-wide text-text-secondary\">\n Actual — by market value\n </p>\n {actualDonut.length > 0 ? (\n <DonutChart\n data={actualDonut}\n centerValue={formatCompact(totalMarketValue, currency)}\n centerLabel=\"Market value\"\n valueFormatter={(v) => formatMoney(v, currency)}\n />\n ) : (\n <p className=\"py-8 text-center text-sm text-text-secondary\">\n Add holdings to see the live allocation.\n </p>\n )}\n </div>\n <div className=\"space-y-2\">\n <p className=\"text-xs font-medium uppercase tracking-wide text-text-secondary\">\n Target — vs live actual\n </p>\n {targetVsActual.length > 0 ? (\n <AllocationBars data={targetVsActual} />\n ) : (\n <p className=\"py-8 text-center text-sm text-text-secondary\">\n No target allocation set for this portfolio.\n </p>\n )}\n </div>\n </div>\n </GlassCard>\n </FadeIn>\n\n {/* Tags */}\n <Card>\n <CardContent className=\"p-4\">\n <TagField entityType=\"brainyrich-portfolio\" entityId={portfolio.id} />\n </CardContent>\n </Card>\n\n {/* Holdings table */}\n <Card>\n <CardHeader className=\"flex flex-row items-center justify-between\">\n <div>\n <CardTitle className=\"text-base\">Holdings</CardTitle>\n <CardDescription>Positions held in this portfolio</CardDescription>\n </div>\n <Button size=\"sm\" onClick={openAddHolding}>\n <Plus className=\"size-4\" aria-hidden=\"true\" />\n Add holding\n </Button>\n </CardHeader>\n <CardContent>\n {holdings.length === 0 ? (\n <EmptyState\n icon={<Briefcase className=\"size-6\" aria-hidden=\"true\" />}\n title=\"No holdings yet\"\n description=\"Add positions to start tracking this portfolio.\"\n action={\n <Button size=\"sm\" onClick={openAddHolding}>\n <Plus className=\"size-4\" aria-hidden=\"true\" />\n Add holding\n </Button>\n }\n />\n ) : (\n <div className=\"overflow-x-auto\">\n <Table>\n <TableHeader>\n <TableRow>\n <TableHead>Symbol</TableHead>\n <TableHead>Class</TableHead>\n <TableHead className=\"text-right\">Qty</TableHead>\n <TableHead className=\"text-right\">Price</TableHead>\n <TableHead className=\"text-right\">Market value</TableHead>\n <TableHead className=\"text-right\">Gain</TableHead>\n <TableHead className=\"w-10\" />\n </TableRow>\n </TableHeader>\n <TableBody>\n {holdings.map((h) => {\n const gain = h.marketValue - h.costBasis;\n const gainPctRow = h.costBasis > 0 ? (gain / h.costBasis) * 100 : 0;\n return (\n <TableRow\n key={h.id}\n className=\"transition-colors motion-safe:hover:bg-bg-sunken/60\"\n >\n <TableCell>\n <div className=\"font-semibold text-text-primary\">{h.symbol}</div>\n {h.name ? (\n <div className=\"text-xs text-text-secondary\">{h.name}</div>\n ) : null}\n </TableCell>\n <TableCell>\n <Badge variant=\"outline\">{prettyEnum(h.assetClass)}</Badge>\n </TableCell>\n <TableCell className=\"text-right tabular-nums\">\n {h.quantity}\n </TableCell>\n <TableCell className=\"text-right tabular-nums\">\n {formatMoney(h.lastPrice, h.currency)}\n </TableCell>\n <TableCell className=\"text-right font-medium tabular-nums\">\n {formatMoney(h.marketValue, h.currency)}\n </TableCell>\n <TableCell className=\"text-right tabular-nums\">\n <GainValue value={gain} currency={h.currency} />\n {h.costBasis > 0 ? (\n <div className=\"text-xs text-text-secondary\">\n {gain > 0 ? '+' : ''}\n {formatPct(gainPctRow)}\n </div>\n ) : null}\n </TableCell>\n <TableCell className=\"text-right\">\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button\n variant=\"ghost\"\n size=\"icon\"\n className=\"size-10\"\n aria-label={`Actions for ${h.symbol}`}\n >\n <MoreHorizontal className=\"size-4\" aria-hidden=\"true\" />\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\">\n <DropdownMenuLabel>{h.symbol}</DropdownMenuLabel>\n <DropdownMenuSeparator />\n <DropdownMenuItem\n className=\"gap-2\"\n onClick={() => openEditHolding(h)}\n >\n <Pencil className=\"size-4\" aria-hidden=\"true\" />\n Edit\n </DropdownMenuItem>\n <DropdownMenuItem\n className=\"gap-2 text-status-error-text focus:text-status-error-text\"\n onClick={() => setHoldingToDelete(h)}\n >\n <Trash2 className=\"size-4\" aria-hidden=\"true\" />\n Delete\n </DropdownMenuItem>\n </DropdownMenuContent>\n </DropdownMenu>\n </TableCell>\n </TableRow>\n );\n })}\n </TableBody>\n </Table>\n </div>\n )}\n </CardContent>\n </Card>\n\n {/* Tags — real UniversalTag assignments keyed by entity */}\n <Card>\n <CardHeader className=\"pb-3\">\n <CardTitle className=\"text-sm\">Tags</CardTitle>\n <CardDescription>Organize and filter this portfolio.</CardDescription>\n </CardHeader>\n <CardContent>\n <TagField entityType=\"brainyrich-portfolio\" entityId={portfolio.id} hideLabel />\n </CardContent>\n </Card>\n\n {/* Discussion — threaded comments keyed by entity */}\n <Card>\n <CardContent className=\"pt-6\">\n <CommentThread entityType=\"brainyrich-portfolio\" entityId={portfolio.id} />\n </CardContent>\n </Card>\n </div>\n\n <HoldingFormDialog\n open={holdingDialogOpen}\n onOpenChange={setHoldingDialogOpen}\n portfolioId={portfolio.id}\n editing={editingHolding}\n />\n\n <ConfirmDialog\n open={holdingToDelete !== null}\n onOpenChange={(open) => {\n if (!open) setHoldingToDelete(null);\n }}\n title=\"Delete holding?\"\n description={\n holdingToDelete\n ? `Remove ${holdingToDelete.symbol} from this portfolio. This cannot be undone.`\n : undefined\n }\n confirmLabel=\"Delete\"\n pendingLabel=\"Deleting…\"\n destructive\n pending={deleteHolding.isPending}\n onConfirm={() => {\n if (!holdingToDelete) return;\n deleteHolding.mutate(holdingToDelete.id, {\n onSuccess: () => setHoldingToDelete(null),\n });\n }}\n />\n </>\n )}\n </SheetContent>\n </Sheet>\n );\n}\n\n// ============================================================================\n// Portfolio card — allocation donut + drift gauge (list grid)\n// ============================================================================\n\nfunction PortfolioCard({\n portfolio: p,\n index,\n onView,\n onEdit,\n onArchive,\n}: {\n portfolio: BraPortfolio;\n index: number;\n onView: () => void;\n onEdit: () => void;\n onArchive: () => void;\n}) {\n const donut = useMemo(() => targetAllocationDonut(p.targetAllocation), [p.targetAllocation]);\n const top = donut[0];\n const tags = p.tags ?? [];\n\n return (\n <FadeIn index={index} as=\"div\" className=\"h-full\">\n <Card className=\"flex h-full flex-col transition-shadow motion-safe:hover:shadow-[var(--shadow-pop-hover,var(--shadow-elevation-2))]\">\n <CardHeader className=\"pb-3\">\n <div className=\"flex items-start justify-between gap-2\">\n <div className=\"flex min-w-0 items-center gap-2\">\n <span className=\"flex size-8 shrink-0 items-center justify-center rounded-lg bg-[var(--color-action-primary-bg)]/12 text-[var(--color-action-primary-text)]\">\n <Briefcase className=\"size-4\" aria-hidden=\"true\" />\n </span>\n <CardTitle className=\"truncate text-base\">{p.name}</CardTitle>\n <Badge variant={statusBadgeVariant(p.status)}>{prettyEnum(p.status)}</Badge>\n </div>\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button\n variant=\"ghost\"\n size=\"icon\"\n className=\"size-9 shrink-0\"\n aria-label={`Actions for ${p.name}`}\n >\n <MoreHorizontal className=\"size-4\" aria-hidden=\"true\" />\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\">\n <DropdownMenuLabel>{p.name}</DropdownMenuLabel>\n <DropdownMenuSeparator />\n <DropdownMenuItem className=\"gap-2\" onClick={onEdit}>\n <Pencil className=\"size-4\" aria-hidden=\"true\" />\n Edit\n </DropdownMenuItem>\n <DropdownMenuItem\n className=\"gap-2 text-status-error-text focus:text-status-error-text\"\n disabled={p.status === 'ARCHIVED'}\n onClick={onArchive}\n >\n <Trash2 className=\"size-4\" aria-hidden=\"true\" />\n Archive\n </DropdownMenuItem>\n </DropdownMenuContent>\n </DropdownMenu>\n </div>\n {p.description?.trim() ? (\n <CardDescription className=\"line-clamp-2\">{p.description}</CardDescription>\n ) : null}\n </CardHeader>\n\n <CardContent className=\"flex flex-1 flex-col justify-between gap-4\">\n <div className=\"grid grid-cols-[auto_1fr] items-center gap-4\">\n {donut.length > 0 ? (\n <div className=\"w-28 shrink-0\">\n <DonutChart\n data={donut}\n legend={false}\n height={112}\n centerValue={top ? formatPct(top.value, 0) : undefined}\n centerLabel={top?.label}\n valueFormatter={(v) => formatPct(v, 0)}\n />\n </div>\n ) : (\n <div className=\"flex size-28 shrink-0 items-center justify-center rounded-full border border-dashed border-border-subtle text-center text-[10px] leading-tight text-text-secondary\">\n No target\n <br />\n allocation\n </div>\n )}\n <div className=\"min-w-0 space-y-2\">\n <div className=\"flex flex-wrap items-center gap-1.5\">\n <Badge variant=\"outline\">{p.baseCurrency}</Badge>\n <Badge variant=\"secondary\">Drift ≤ {formatPct(p.driftThresholdPct)}</Badge>\n </div>\n {donut.length > 0 ? (\n <ul className=\"space-y-1\" aria-label=\"Target allocation\">\n {donut.slice(0, 3).map((d) => (\n <li key={d.label} className=\"flex items-center gap-1.5 text-xs\">\n <span className=\"truncate text-text-secondary\">{d.label}</span>\n <span className=\"ml-auto font-medium tabular-nums text-text-primary\">\n {formatPct(d.value, 0)}\n </span>\n </li>\n ))}\n </ul>\n ) : null}\n {tags.length > 0 ? (\n <div className=\"flex flex-wrap gap-1\">\n {tags.slice(0, 3).map((t) => (\n <Badge key={t} variant=\"outline\" className=\"text-[10px]\">\n {t}\n </Badge>\n ))}\n </div>\n ) : null}\n </div>\n </div>\n <Button variant=\"outline\" size=\"sm\" className=\"w-full\" onClick={onView}>\n View holdings\n </Button>\n </CardContent>\n </Card>\n </FadeIn>\n );\n}\n\n// ============================================================================\n// Page\n// ============================================================================\n\nexport function PortfoliosPage() {\n useTrackBrainyRichPageView('brainyrich.portfolios.viewed');\n\n const {\n items: portfolios,\n hasMore,\n loadMore,\n isLoading,\n isFetchingNextPage,\n isError,\n refetch,\n } = usePagedPortfolios();\n const archivePortfolio = useArchivePortfolio();\n\n const [formOpen, setFormOpen] = useState(false);\n const [editingPortfolio, setEditingPortfolio] = useState<BraPortfolio | null>(null);\n const [detailId, setDetailId] = useState<string | null>(null);\n const [portfolioToArchive, setPortfolioToArchive] = useState<BraPortfolio | null>(null);\n\n const activeCount = portfolios.filter((p) => p.status === 'ACTIVE').length;\n const draftCount = portfolios.filter((p) => p.status === 'DRAFT').length;\n const avgDrift =\n portfolios.length > 0\n ? portfolios.reduce((s, p) => s + p.driftThresholdPct, 0) / portfolios.length\n : 0;\n\n const openCreate = () => {\n setEditingPortfolio(null);\n setFormOpen(true);\n };\n const openEdit = (p: BraPortfolio) => {\n setEditingPortfolio(p);\n setFormOpen(true);\n };\n\n return (\n <div data-tour=\"brainyrich-page-portfolios\" className=\"contents\">\n <SectionShell\n title=\"Portfolios\"\n description=\"Organize holdings into strategies and keep allocations on target.\"\n badges={portfolios.length > 0 ? [`${portfolios.length} total`] : []}\n actions={\n <Button size=\"sm\" onClick={openCreate}>\n <Plus className=\"size-4\" aria-hidden=\"true\" />\n New portfolio\n </Button>\n }\n >\n {isLoading ? (\n <LoadingState rows={4} />\n ) : isError ? (\n <ErrorState onRetry={() => refetch()} />\n ) : portfolios.length === 0 ? (\n <EmptyState\n icon={<Briefcase className=\"size-6\" aria-hidden=\"true\" />}\n title=\"No portfolios yet\"\n description=\"Create your first portfolio to start organizing holdings and tracking drift.\"\n action={\n <Button size=\"sm\" onClick={openCreate}>\n <Plus className=\"size-4\" aria-hidden=\"true\" />\n New portfolio\n </Button>\n }\n />\n ) : (\n <>\n <FadeIn className=\"grid grid-cols-2 gap-3 lg:grid-cols-4\">\n <StatTile label=\"Portfolios\" value={String(portfolios.length)} glow />\n <StatTile label=\"Active\" value={String(activeCount)} />\n <StatTile label=\"Drafts\" value={String(draftCount)} />\n <StatTile label=\"Avg drift threshold\" value={formatPct(avgDrift)} />\n </FadeIn>\n\n <div className=\"grid gap-4 md:grid-cols-2 xl:grid-cols-3\">\n {portfolios.map((p, i) => (\n <PortfolioCard\n key={p.id}\n portfolio={p}\n index={i}\n onView={() => setDetailId(p.id)}\n onEdit={() => openEdit(p)}\n onArchive={() => setPortfolioToArchive(p)}\n />\n ))}\n </div>\n\n <LoadMore hasMore={hasMore} onClick={loadMore} loading={isFetchingNextPage} />\n </>\n )}\n\n <PortfolioFormDialog\n open={formOpen}\n onOpenChange={setFormOpen}\n editing={editingPortfolio}\n />\n\n <PortfolioDetailSheet\n portfolioId={detailId}\n onOpenChange={(open) => {\n if (!open) setDetailId(null);\n }}\n />\n\n <ConfirmDialog\n open={portfolioToArchive !== null}\n onOpenChange={(open) => {\n if (!open) setPortfolioToArchive(null);\n }}\n title=\"Archive portfolio?\"\n description={\n portfolioToArchive\n ? `Archive “${portfolioToArchive.name}”. It will move to archived status and stop appearing in active views.`\n : undefined\n }\n confirmLabel=\"Archive\"\n pendingLabel=\"Archiving…\"\n destructive\n pending={archivePortfolio.isPending}\n onConfirm={() => {\n if (!portfolioToArchive) return;\n archivePortfolio.mutate(portfolioToArchive.id, {\n onSuccess: () => {\n hapticSuccess();\n void showToast('Portfolio archived');\n setPortfolioToArchive(null);\n },\n });\n }}\n />\n </SectionShell>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"PortfoliosPage.js","names":[],"sources":["../../../src/brainyrich/pages/PortfoliosPage.tsx"],"sourcesContent":["import { useMemo, useState } from 'react';\nimport {\n Badge,\n Button,\n Card,\n CardContent,\n CardDescription,\n CardHeader,\n CardTitle,\n Dialog,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuLabel,\n DropdownMenuSeparator,\n DropdownMenuTrigger,\n Input,\n Label,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n Sheet,\n SheetContent,\n SheetDescription,\n SheetHeader,\n SheetTitle,\n Table,\n TableBody,\n TableCell,\n TableHead,\n TableHeader,\n TableRow,\n Textarea,\n} from '@burdenoff/fe-libs/ui';\nimport {\n ArrowDownRight,\n ArrowUpRight,\n Briefcase,\n MoreHorizontal,\n Pencil,\n Plus,\n Trash2,\n} from 'lucide-react';\n\nimport {\n useArchivePortfolio,\n useCreateHolding,\n useCreatePortfolio,\n useDeleteHolding,\n useHouseholds,\n useModelPortfolios,\n usePagedPortfolios,\n usePortfolio,\n useUpdateHolding,\n useUpdatePortfolio,\n} from '../hooks/useBrainyRichApi';\nimport {\n AccountPicker,\n ConfirmDialog,\n DonutChart,\n FadeIn,\n GlassCard,\n LoadMore,\n StatTile,\n type DonutDatum,\n type VizTone,\n} from '../components';\nimport { CommentThread } from '../components/reuse/CommentThread';\nimport { TagField } from '../components/reuse/TagField';\nimport { hapticSuccess, showToast } from '../utils/nativeControls';\nimport { useTrackBrainyRichPageView } from '../hooks/useBrainyRichAnalytics';\nimport type { BraAssetClass, BraHolding, BraPortfolio, BraPortfolioStatus } from '../types/bra';\nimport {\n AllocationBars,\n EmptyState,\n ErrorState,\n LoadingState,\n SectionShell,\n formatCompact,\n formatMoney,\n formatPct,\n statusBadgeVariant,\n type AllocationDatum,\n} from './_shared';\n\nconst ASSET_CLASSES: BraAssetClass[] = [\n 'EQUITY',\n 'FIXED_INCOME',\n 'CASH',\n 'REAL_ESTATE',\n 'COMMODITY',\n 'CRYPTO',\n 'ALTERNATIVE',\n];\n\nconst PORTFOLIO_STATUSES: BraPortfolioStatus[] = ['DRAFT', 'ACTIVE', 'ARCHIVED'];\n\nconst NONE = '__none__';\n\nconst prettyEnum = (value: string): string =>\n value\n .toLowerCase()\n .split('_')\n .map((w) => w.charAt(0).toUpperCase() + w.slice(1))\n .join(' ');\n\nconst toNumber = (value: string): number | undefined => {\n if (value.trim() === '') return undefined;\n const n = Number(value);\n return Number.isFinite(n) ? n : undefined;\n};\n\n/** Stable, semantic tone per asset class so donut/legend colours read consistently. */\nconst ASSET_TONE: Record<string, 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\nconst toneForClass = (raw: string): VizTone => ASSET_TONE[raw.toUpperCase()] ?? 'neutral';\n\n/** Read a `targetAllocation` JSON blob as an assetClass → percentage map. */\nfunction parseTargetAllocation(raw: unknown): AllocationDatum[] {\n if (!raw || typeof raw !== 'object') return [];\n return Object.entries(raw as Record<string, unknown>)\n .map(([label, value]) => ({ label, value: Number(value) }))\n .filter((d) => Number.isFinite(d.value))\n .map((d) => ({ label: prettyEnum(d.label), value: d.value, tone: 'accent' as const }));\n}\n\n/** Read a `targetAllocation` JSON blob as tone-keyed donut slices (percent values). */\nfunction targetAllocationDonut(raw: unknown): DonutDatum[] {\n if (!raw || typeof raw !== 'object') return [];\n return Object.entries(raw as Record<string, unknown>)\n .map(([key, value]) => ({ key, value: Number(value) }))\n .filter((d) => Number.isFinite(d.value) && d.value > 0)\n .sort((a, b) => b.value - a.value)\n .map((d) => ({ label: prettyEnum(d.key), value: d.value, tone: toneForClass(d.key) }));\n}\n\n// ============================================================================\n// Gain cell — signed money with icon + semantic color (color never sole signal)\n// ============================================================================\n\nfunction GainValue({ value, currency }: { value: number; currency: string }) {\n const positive = value > 0;\n const negative = value < 0;\n const tone = positive\n ? 'text-status-success-text'\n : negative\n ? 'text-status-error-text'\n : 'text-text-secondary';\n const Icon = positive ? ArrowUpRight : negative ? ArrowDownRight : null;\n return (\n <span className={`inline-flex items-center justify-end gap-1 font-medium ${tone}`}>\n {Icon ? <Icon className=\"size-3.5\" aria-hidden=\"true\" /> : null}\n {positive ? '+' : ''}\n {formatMoney(value, currency)}\n </span>\n );\n}\n\n// ============================================================================\n// Portfolio create / edit dialog\n// ============================================================================\n\ninterface PortfolioFormState {\n name: string;\n baseCurrency: string;\n description: string;\n driftThresholdPct: string;\n status: BraPortfolioStatus;\n modelPortfolioId: string;\n householdId: string;\n}\n\nfunction emptyForm(): PortfolioFormState {\n return {\n name: '',\n baseCurrency: 'USD',\n description: '',\n driftThresholdPct: '5',\n status: 'DRAFT',\n modelPortfolioId: NONE,\n householdId: NONE,\n };\n}\n\nfunction PortfolioFormDialog({\n open,\n onOpenChange,\n editing,\n}: {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n editing: BraPortfolio | null;\n}) {\n const { data: models } = useModelPortfolios();\n const { data: households } = useHouseholds();\n const createPortfolio = useCreatePortfolio();\n const updatePortfolio = useUpdatePortfolio();\n\n const [form, setForm] = useState<PortfolioFormState>(emptyForm);\n // Re-seed the form whenever the dialog target changes.\n const [seededFor, setSeededFor] = useState<string | null>(null);\n const targetKey = editing?.id ?? 'new';\n if (open && seededFor !== targetKey) {\n setSeededFor(targetKey);\n setForm(\n editing\n ? {\n name: editing.name,\n baseCurrency: editing.baseCurrency,\n description: editing.description ?? '',\n driftThresholdPct: String(editing.driftThresholdPct),\n status: editing.status,\n modelPortfolioId: editing.modelPortfolioId ?? NONE,\n householdId: editing.householdId ?? NONE,\n }\n : emptyForm()\n );\n }\n if (!open && seededFor !== null) {\n setSeededFor(null);\n }\n\n const pending = createPortfolio.isPending || updatePortfolio.isPending;\n const nameValid = form.name.trim().length > 0;\n\n const handleSubmit = () => {\n if (!nameValid) return;\n const drift = toNumber(form.driftThresholdPct);\n const modelPortfolioId = form.modelPortfolioId === NONE ? null : form.modelPortfolioId;\n const householdId = form.householdId === NONE ? null : form.householdId;\n\n if (editing) {\n updatePortfolio.mutate(\n {\n id: editing.id,\n input: {\n name: form.name.trim(),\n baseCurrency: form.baseCurrency.trim() || 'USD',\n description: form.description.trim() || null,\n driftThresholdPct: drift,\n status: form.status,\n modelPortfolioId,\n },\n },\n {\n onSuccess: () => {\n hapticSuccess();\n void showToast('Portfolio saved');\n onOpenChange(false);\n },\n }\n );\n } else {\n createPortfolio.mutate(\n {\n name: form.name.trim(),\n baseCurrency: form.baseCurrency.trim() || 'USD',\n description: form.description.trim() || null,\n driftThresholdPct: drift,\n modelPortfolioId,\n householdId,\n },\n {\n onSuccess: () => {\n hapticSuccess();\n void showToast('Portfolio created');\n onOpenChange(false);\n },\n }\n );\n }\n };\n\n return (\n <Dialog open={open} onOpenChange={onOpenChange}>\n <DialogContent className=\"max-h-[90vh] overflow-y-auto sm:max-w-lg\">\n <DialogHeader>\n <DialogTitle>{editing ? 'Edit portfolio' : 'New portfolio'}</DialogTitle>\n <DialogDescription>\n {editing\n ? 'Update this portfolio’s strategy settings.'\n : 'Create an investment portfolio to organize and rebalance holdings.'}\n </DialogDescription>\n </DialogHeader>\n\n <div className=\"space-y-4 py-2\">\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"pf-name\">Name</Label>\n <Input\n id=\"pf-name\"\n value={form.name}\n onChange={(e) => setForm((f) => ({ ...f, name: e.target.value }))}\n placeholder=\"Growth Portfolio\"\n aria-invalid={!nameValid}\n />\n </div>\n\n <div className=\"grid grid-cols-2 gap-4\">\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"pf-currency\">Base currency</Label>\n <Input\n id=\"pf-currency\"\n value={form.baseCurrency}\n onChange={(e) =>\n setForm((f) => ({ ...f, baseCurrency: e.target.value.toUpperCase() }))\n }\n placeholder=\"USD\"\n maxLength={3}\n />\n </div>\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"pf-drift\">Drift threshold %</Label>\n <Input\n id=\"pf-drift\"\n type=\"number\"\n min={0}\n step=\"0.5\"\n value={form.driftThresholdPct}\n onChange={(e) => setForm((f) => ({ ...f, driftThresholdPct: e.target.value }))}\n />\n </div>\n </div>\n\n {editing ? (\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"pf-status\">Status</Label>\n <Select\n value={form.status}\n onValueChange={(v) => setForm((f) => ({ ...f, status: v as BraPortfolioStatus }))}\n >\n <SelectTrigger id=\"pf-status\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {PORTFOLIO_STATUSES.map((s) => (\n <SelectItem key={s} value={s}>\n {prettyEnum(s)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n ) : null}\n\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"pf-model\">Model portfolio</Label>\n <Select\n value={form.modelPortfolioId}\n onValueChange={(v) => setForm((f) => ({ ...f, modelPortfolioId: v }))}\n >\n <SelectTrigger id=\"pf-model\">\n <SelectValue placeholder=\"None\" />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value={NONE}>None</SelectItem>\n {models.map((m) => (\n <SelectItem key={m.id} value={m.id}>\n {m.name}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n\n {!editing ? (\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"pf-household\">Household</Label>\n <Select\n value={form.householdId}\n onValueChange={(v) => setForm((f) => ({ ...f, householdId: v }))}\n >\n <SelectTrigger id=\"pf-household\">\n <SelectValue placeholder=\"None\" />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value={NONE}>None</SelectItem>\n {households.map((h) => (\n <SelectItem key={h.id} value={h.id}>\n {h.name}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n ) : null}\n\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"pf-desc\">Description</Label>\n <Textarea\n id=\"pf-desc\"\n value={form.description}\n onChange={(e) => setForm((f) => ({ ...f, description: e.target.value }))}\n placeholder=\"Optional notes about this strategy\"\n rows={3}\n />\n </div>\n </div>\n\n <DialogFooter>\n <Button variant=\"outline\" onClick={() => onOpenChange(false)} disabled={pending}>\n Cancel\n </Button>\n <Button onClick={handleSubmit} disabled={!nameValid || pending}>\n {pending ? 'Saving…' : editing ? 'Save changes' : 'Create portfolio'}\n </Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n );\n}\n\n// ============================================================================\n// Holding create / edit dialog (scoped to a portfolio)\n// ============================================================================\n\ninterface HoldingFormState {\n accountId: string;\n symbol: string;\n name: string;\n assetClass: BraAssetClass;\n quantity: string;\n lastPrice: string;\n costBasis: string;\n}\n\nfunction emptyHoldingForm(): HoldingFormState {\n return {\n accountId: '',\n symbol: '',\n name: '',\n assetClass: 'EQUITY',\n quantity: '0',\n lastPrice: '0',\n costBasis: '0',\n };\n}\n\nfunction HoldingFormDialog({\n open,\n onOpenChange,\n portfolioId,\n editing,\n}: {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n portfolioId: string;\n editing: BraHolding | null;\n}) {\n const createHolding = useCreateHolding();\n const updateHolding = useUpdateHolding();\n\n const [form, setForm] = useState<HoldingFormState>(emptyHoldingForm);\n const [seededFor, setSeededFor] = useState<string | null>(null);\n const targetKey = editing?.id ?? 'new';\n if (open && seededFor !== targetKey) {\n setSeededFor(targetKey);\n setForm(\n editing\n ? {\n accountId: editing.accountId,\n symbol: editing.symbol,\n name: editing.name ?? '',\n assetClass: editing.assetClass,\n quantity: String(editing.quantity),\n lastPrice: String(editing.lastPrice),\n costBasis: String(editing.costBasis),\n }\n : emptyHoldingForm()\n );\n }\n if (!open && seededFor !== null) {\n setSeededFor(null);\n }\n\n const pending = createHolding.isPending || updateHolding.isPending;\n const symbolValid = form.symbol.trim().length > 0;\n const accountValid = editing ? true : form.accountId.trim().length > 0;\n const canSubmit = symbolValid && accountValid && !pending;\n\n const handleSubmit = () => {\n if (!symbolValid || !accountValid) return;\n if (editing) {\n updateHolding.mutate(\n {\n id: editing.id,\n input: {\n name: form.name.trim() || null,\n assetClass: form.assetClass,\n quantity: toNumber(form.quantity),\n lastPrice: toNumber(form.lastPrice),\n costBasis: toNumber(form.costBasis),\n },\n },\n {\n onSuccess: () => {\n hapticSuccess();\n void showToast('Holding saved');\n onOpenChange(false);\n },\n }\n );\n } else {\n createHolding.mutate(\n {\n accountId: form.accountId,\n portfolioId,\n symbol: form.symbol.trim().toUpperCase(),\n name: form.name.trim() || null,\n assetClass: form.assetClass,\n quantity: toNumber(form.quantity),\n lastPrice: toNumber(form.lastPrice),\n costBasis: toNumber(form.costBasis),\n },\n {\n onSuccess: () => {\n hapticSuccess();\n void showToast('Holding added');\n onOpenChange(false);\n },\n }\n );\n }\n };\n\n return (\n <Dialog open={open} onOpenChange={onOpenChange}>\n <DialogContent className=\"max-h-[90vh] overflow-y-auto sm:max-w-lg\">\n <DialogHeader>\n <DialogTitle>{editing ? 'Edit holding' : 'Add holding'}</DialogTitle>\n <DialogDescription>\n {editing\n ? 'Update the position details for this holding.'\n : 'Add a position to this portfolio from one of your accounts.'}\n </DialogDescription>\n </DialogHeader>\n\n <div className=\"space-y-4 py-2\">\n {!editing ? (\n <AccountPicker\n id=\"h-account\"\n value={form.accountId}\n onChange={(v) => setForm((f) => ({ ...f, accountId: v }))}\n placeholder=\"Select account\"\n />\n ) : null}\n\n <div className=\"grid grid-cols-2 gap-4\">\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"h-symbol\">Symbol</Label>\n <Input\n id=\"h-symbol\"\n value={form.symbol}\n onChange={(e) => setForm((f) => ({ ...f, symbol: e.target.value }))}\n placeholder=\"AAPL\"\n disabled={Boolean(editing)}\n aria-invalid={!symbolValid}\n />\n </div>\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"h-class\">Asset class</Label>\n <Select\n value={form.assetClass}\n onValueChange={(v) => setForm((f) => ({ ...f, assetClass: v as BraAssetClass }))}\n >\n <SelectTrigger id=\"h-class\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {ASSET_CLASSES.map((c) => (\n <SelectItem key={c} value={c}>\n {prettyEnum(c)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n </div>\n\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"h-name\">Name</Label>\n <Input\n id=\"h-name\"\n value={form.name}\n onChange={(e) => setForm((f) => ({ ...f, name: e.target.value }))}\n placeholder=\"Apple Inc.\"\n />\n </div>\n\n <div className=\"grid grid-cols-3 gap-4\">\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"h-qty\">Quantity</Label>\n <Input\n id=\"h-qty\"\n type=\"number\"\n step=\"any\"\n value={form.quantity}\n onChange={(e) => setForm((f) => ({ ...f, quantity: e.target.value }))}\n />\n </div>\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"h-price\">Last price</Label>\n <Input\n id=\"h-price\"\n type=\"number\"\n step=\"any\"\n value={form.lastPrice}\n onChange={(e) => setForm((f) => ({ ...f, lastPrice: e.target.value }))}\n />\n </div>\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"h-cost\">Cost basis</Label>\n <Input\n id=\"h-cost\"\n type=\"number\"\n step=\"any\"\n value={form.costBasis}\n onChange={(e) => setForm((f) => ({ ...f, costBasis: e.target.value }))}\n />\n </div>\n </div>\n </div>\n\n <DialogFooter>\n <Button variant=\"outline\" onClick={() => onOpenChange(false)} disabled={pending}>\n Cancel\n </Button>\n <Button onClick={handleSubmit} disabled={!canSubmit}>\n {pending ? 'Saving…' : editing ? 'Save changes' : 'Add holding'}\n </Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n );\n}\n\n// ============================================================================\n// Portfolio detail sheet — holdings table + target vs actual allocation\n// ============================================================================\n\nfunction PortfolioDetailSheet({\n portfolioId,\n onOpenChange,\n}: {\n portfolioId: string | null;\n onOpenChange: (open: boolean) => void;\n}) {\n const { data: portfolio, isLoading, isError, refetch } = usePortfolio(portfolioId ?? undefined);\n const deleteHolding = useDeleteHolding();\n\n const [holdingDialogOpen, setHoldingDialogOpen] = useState(false);\n const [editingHolding, setEditingHolding] = useState<BraHolding | null>(null);\n const [holdingToDelete, setHoldingToDelete] = useState<BraHolding | null>(null);\n\n const holdings = portfolio?.holdings ?? [];\n const currency = portfolio?.baseCurrency ?? 'USD';\n const totalMarketValue = holdings.reduce((s, h) => s + h.marketValue, 0);\n const totalCostBasis = holdings.reduce((s, h) => s + h.costBasis, 0);\n const totalGain = totalMarketValue - totalCostBasis;\n const gainPct = totalCostBasis > 0 ? (totalGain / totalCostBasis) * 100 : 0;\n\n /** Live allocation by class in base-currency value — tone-keyed donut slices. */\n const actualDonut: DonutDatum[] = useMemo(() => {\n if (holdings.length === 0 || totalMarketValue <= 0) return [];\n const byClass = new Map<string, number>();\n holdings.forEach((h) =>\n byClass.set(h.assetClass, (byClass.get(h.assetClass) ?? 0) + h.marketValue)\n );\n return Array.from(byClass.entries())\n .map(([cls, mv]) => ({ label: prettyEnum(cls), value: mv, tone: toneForClass(cls) }))\n .sort((a, b) => b.value - a.value);\n }, [holdings, totalMarketValue]);\n\n /** Actual allocation as percentages, keyed by pretty class label, for drift comparison. */\n const actualPctByLabel = useMemo(() => {\n const map = new Map<string, number>();\n if (totalMarketValue <= 0) return map;\n holdings.forEach((h) =>\n map.set(\n prettyEnum(h.assetClass),\n (map.get(prettyEnum(h.assetClass)) ?? 0) + (h.marketValue / totalMarketValue) * 100\n )\n );\n return map;\n }, [holdings, totalMarketValue]);\n\n /** Target vs actual: each target row captioned with the live actual weight. */\n const targetVsActual: AllocationDatum[] = useMemo(() => {\n return parseTargetAllocation(portfolio?.targetAllocation).map((d) => {\n const actual = actualPctByLabel.get(d.label) ?? 0;\n return {\n label: d.label,\n value: d.value,\n tone: 'accent' as const,\n caption: `${formatPct(d.value, 0)} target · ${formatPct(actual, 0)} actual`,\n };\n });\n }, [portfolio?.targetAllocation, actualPctByLabel]);\n\n const openAddHolding = () => {\n setEditingHolding(null);\n setHoldingDialogOpen(true);\n };\n const openEditHolding = (h: BraHolding) => {\n setEditingHolding(h);\n setHoldingDialogOpen(true);\n };\n\n return (\n <Sheet open={portfolioId !== null} onOpenChange={onOpenChange}>\n <SheetContent className=\"flex w-full flex-col gap-0 overflow-y-auto p-0 sm:max-w-2xl\">\n {isLoading ? (\n <div className=\"p-6\">\n <LoadingState rows={5} />\n </div>\n ) : isError ? (\n <div className=\"p-6\">\n <ErrorState onRetry={() => refetch()} />\n </div>\n ) : !portfolio ? (\n <div className=\"p-6\">\n <EmptyState\n icon={<Briefcase className=\"size-6\" aria-hidden=\"true\" />}\n title=\"Portfolio not found\"\n description=\"This portfolio may have been archived or removed.\"\n />\n </div>\n ) : (\n <>\n <SheetHeader className=\"border-b border-border-subtle p-6\">\n <div className=\"flex items-center gap-2\">\n <SheetTitle className=\"text-xl\">{portfolio.name}</SheetTitle>\n <Badge variant={statusBadgeVariant(portfolio.status)}>\n {prettyEnum(portfolio.status)}\n </Badge>\n </div>\n <SheetDescription>\n {portfolio.description?.trim()\n ? portfolio.description\n : `${portfolio.baseCurrency} · Drift threshold ${formatPct(portfolio.driftThresholdPct)}`}\n </SheetDescription>\n </SheetHeader>\n\n <div className=\"space-y-6 p-6\">\n {/* Hero KPIs — market value carries an emerald glow + live gain delta */}\n <FadeIn className=\"grid grid-cols-2 gap-3 lg:grid-cols-4\">\n <StatTile\n label=\"Market value\"\n value={formatMoney(totalMarketValue, currency)}\n glow\n />\n <StatTile\n label=\"Unrealized gain\"\n value={formatMoney(totalGain, currency)}\n delta={totalCostBasis > 0 ? gainPct : undefined}\n deltaTone={totalGain > 0 ? 'positive' : totalGain < 0 ? 'negative' : 'neutral'}\n />\n <StatTile label=\"Holdings\" value={String(holdings.length)} />\n <StatTile label=\"Drift threshold\" value={formatPct(portfolio.driftThresholdPct)} />\n </FadeIn>\n\n {/* Target vs actual allocation — live donut + drift-aware target bars */}\n <FadeIn index={1}>\n <GlassCard glow className=\"space-y-5\">\n <div className=\"flex items-center justify-between gap-2\">\n <h3 className=\"text-sm font-semibold text-text-primary\">\n Target vs actual allocation\n </h3>\n <Badge variant=\"outline\">{currency}</Badge>\n </div>\n <div className=\"grid gap-6 md:grid-cols-2\">\n <div className=\"space-y-2\">\n <p className=\"text-xs font-medium uppercase tracking-wide text-text-secondary\">\n Actual — by market value\n </p>\n {actualDonut.length > 0 ? (\n <DonutChart\n data={actualDonut}\n centerValue={formatCompact(totalMarketValue, currency)}\n centerLabel=\"Market value\"\n valueFormatter={(v) => formatMoney(v, currency)}\n />\n ) : (\n <p className=\"py-8 text-center text-sm text-text-secondary\">\n Add holdings to see the live allocation.\n </p>\n )}\n </div>\n <div className=\"space-y-2\">\n <p className=\"text-xs font-medium uppercase tracking-wide text-text-secondary\">\n Target — vs live actual\n </p>\n {targetVsActual.length > 0 ? (\n <AllocationBars data={targetVsActual} />\n ) : (\n <p className=\"py-8 text-center text-sm text-text-secondary\">\n No target allocation set for this portfolio.\n </p>\n )}\n </div>\n </div>\n </GlassCard>\n </FadeIn>\n\n {/* Tags */}\n <Card>\n <CardContent className=\"p-4\">\n <TagField entityType=\"brainyrich-portfolio\" entityId={portfolio.id} />\n </CardContent>\n </Card>\n\n {/* Holdings table */}\n <Card>\n <CardHeader className=\"flex flex-row items-center justify-between\">\n <div>\n <CardTitle className=\"text-base\">Holdings</CardTitle>\n <CardDescription>Positions held in this portfolio</CardDescription>\n </div>\n <Button size=\"sm\" onClick={openAddHolding}>\n <Plus className=\"size-4\" aria-hidden=\"true\" />\n Add holding\n </Button>\n </CardHeader>\n <CardContent>\n {holdings.length === 0 ? (\n <EmptyState\n icon={<Briefcase className=\"size-6\" aria-hidden=\"true\" />}\n title=\"No holdings yet\"\n description=\"Add positions to start tracking this portfolio.\"\n action={\n <Button size=\"sm\" onClick={openAddHolding}>\n <Plus className=\"size-4\" aria-hidden=\"true\" />\n Add holding\n </Button>\n }\n />\n ) : (\n <div className=\"overflow-x-auto\">\n <Table>\n <TableHeader>\n <TableRow>\n <TableHead>Symbol</TableHead>\n <TableHead>Class</TableHead>\n <TableHead className=\"text-right\">Qty</TableHead>\n <TableHead className=\"text-right\">Price</TableHead>\n <TableHead className=\"text-right\">Market value</TableHead>\n <TableHead className=\"text-right\">Gain</TableHead>\n <TableHead className=\"w-10\" />\n </TableRow>\n </TableHeader>\n <TableBody>\n {holdings.map((h) => {\n const gain = h.marketValue - h.costBasis;\n const gainPctRow = h.costBasis > 0 ? (gain / h.costBasis) * 100 : 0;\n return (\n <TableRow\n key={h.id}\n className=\"transition-colors motion-safe:hover:bg-bg-sunken/60\"\n >\n <TableCell>\n <div className=\"font-semibold text-text-primary\">{h.symbol}</div>\n {h.name ? (\n <div className=\"text-xs text-text-secondary\">{h.name}</div>\n ) : null}\n </TableCell>\n <TableCell>\n <Badge variant=\"outline\">{prettyEnum(h.assetClass)}</Badge>\n </TableCell>\n <TableCell className=\"text-right tabular-nums\">\n {h.quantity}\n </TableCell>\n <TableCell className=\"text-right tabular-nums\">\n {formatMoney(h.lastPrice, h.currency)}\n </TableCell>\n <TableCell className=\"text-right font-medium tabular-nums\">\n {formatMoney(h.marketValue, h.currency)}\n </TableCell>\n <TableCell className=\"text-right tabular-nums\">\n <GainValue value={gain} currency={h.currency} />\n {h.costBasis > 0 ? (\n <div className=\"text-xs text-text-secondary\">\n {gain > 0 ? '+' : ''}\n {formatPct(gainPctRow)}\n </div>\n ) : null}\n </TableCell>\n <TableCell className=\"text-right\">\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button\n variant=\"ghost\"\n size=\"icon\"\n className=\"size-10\"\n aria-label={`Actions for ${h.symbol}`}\n >\n <MoreHorizontal className=\"size-4\" aria-hidden=\"true\" />\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\">\n <DropdownMenuLabel>{h.symbol}</DropdownMenuLabel>\n <DropdownMenuSeparator />\n <DropdownMenuItem\n className=\"gap-2\"\n onClick={() => openEditHolding(h)}\n >\n <Pencil className=\"size-4\" aria-hidden=\"true\" />\n Edit\n </DropdownMenuItem>\n <DropdownMenuItem\n className=\"gap-2 text-status-error-text focus:text-status-error-text\"\n onClick={() => setHoldingToDelete(h)}\n >\n <Trash2 className=\"size-4\" aria-hidden=\"true\" />\n Delete\n </DropdownMenuItem>\n </DropdownMenuContent>\n </DropdownMenu>\n </TableCell>\n </TableRow>\n );\n })}\n </TableBody>\n </Table>\n </div>\n )}\n </CardContent>\n </Card>\n\n {/* Tags — real UniversalTag assignments keyed by entity */}\n <Card>\n <CardHeader className=\"pb-3\">\n <CardTitle className=\"text-sm\">Tags</CardTitle>\n <CardDescription>Organize and filter this portfolio.</CardDescription>\n </CardHeader>\n <CardContent>\n <TagField entityType=\"brainyrich-portfolio\" entityId={portfolio.id} hideLabel />\n </CardContent>\n </Card>\n\n {/* Discussion — threaded comments keyed by entity */}\n <Card>\n <CardContent className=\"pt-6\">\n <CommentThread entityType=\"brainyrich-portfolio\" entityId={portfolio.id} />\n </CardContent>\n </Card>\n </div>\n\n <HoldingFormDialog\n open={holdingDialogOpen}\n onOpenChange={setHoldingDialogOpen}\n portfolioId={portfolio.id}\n editing={editingHolding}\n />\n\n <ConfirmDialog\n open={holdingToDelete !== null}\n onOpenChange={(open) => {\n if (!open) setHoldingToDelete(null);\n }}\n title=\"Delete holding?\"\n description={\n holdingToDelete\n ? `Remove ${holdingToDelete.symbol} from this portfolio. This cannot be undone.`\n : undefined\n }\n confirmLabel=\"Delete\"\n pendingLabel=\"Deleting…\"\n destructive\n pending={deleteHolding.isPending}\n onConfirm={() => {\n if (!holdingToDelete) return;\n deleteHolding.mutate(holdingToDelete.id, {\n onSuccess: () => setHoldingToDelete(null),\n });\n }}\n />\n </>\n )}\n </SheetContent>\n </Sheet>\n );\n}\n\n// ============================================================================\n// Portfolio card — allocation donut + drift gauge (list grid)\n// ============================================================================\n\nfunction PortfolioCard({\n portfolio: p,\n index,\n onView,\n onEdit,\n onArchive,\n}: {\n portfolio: BraPortfolio;\n index: number;\n onView: () => void;\n onEdit: () => void;\n onArchive: () => void;\n}) {\n const donut = useMemo(() => targetAllocationDonut(p.targetAllocation), [p.targetAllocation]);\n const top = donut[0];\n const tags = p.tags ?? [];\n\n return (\n <FadeIn index={index} as=\"div\" className=\"h-full\">\n <Card className=\"flex h-full flex-col transition-shadow motion-safe:hover:shadow-[var(--shadow-pop-hover,var(--shadow-elevation-2))]\">\n <CardHeader className=\"pb-3\">\n <div className=\"flex items-start justify-between gap-2\">\n <div className=\"flex min-w-0 items-center gap-2\">\n <span className=\"flex size-8 shrink-0 items-center justify-center rounded-lg bg-[var(--color-action-primary-bg)]/12 text-[var(--color-action-primary-text)]\">\n <Briefcase className=\"size-4\" aria-hidden=\"true\" />\n </span>\n <CardTitle className=\"truncate text-base\">{p.name}</CardTitle>\n <Badge variant={statusBadgeVariant(p.status)}>{prettyEnum(p.status)}</Badge>\n </div>\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button\n variant=\"ghost\"\n size=\"icon\"\n className=\"size-9 shrink-0\"\n aria-label={`Actions for ${p.name}`}\n >\n <MoreHorizontal className=\"size-4\" aria-hidden=\"true\" />\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\">\n <DropdownMenuLabel>{p.name}</DropdownMenuLabel>\n <DropdownMenuSeparator />\n <DropdownMenuItem className=\"gap-2\" onClick={onEdit}>\n <Pencil className=\"size-4\" aria-hidden=\"true\" />\n Edit\n </DropdownMenuItem>\n <DropdownMenuItem\n className=\"gap-2 text-status-error-text focus:text-status-error-text\"\n disabled={p.status === 'ARCHIVED'}\n onClick={onArchive}\n >\n <Trash2 className=\"size-4\" aria-hidden=\"true\" />\n Archive\n </DropdownMenuItem>\n </DropdownMenuContent>\n </DropdownMenu>\n </div>\n {p.description?.trim() ? (\n <CardDescription className=\"line-clamp-2\">{p.description}</CardDescription>\n ) : null}\n </CardHeader>\n\n <CardContent className=\"flex flex-1 flex-col justify-between gap-4\">\n <div className=\"grid grid-cols-[auto_1fr] items-center gap-4\">\n {donut.length > 0 ? (\n <div className=\"w-28 shrink-0\">\n <DonutChart\n data={donut}\n legend={false}\n height={112}\n centerValue={top ? formatPct(top.value, 0) : undefined}\n centerLabel={top?.label}\n valueFormatter={(v) => formatPct(v, 0)}\n />\n </div>\n ) : (\n <div className=\"flex size-28 shrink-0 items-center justify-center rounded-full border border-dashed border-border-subtle text-center text-[10px] leading-tight text-text-secondary\">\n No target\n <br />\n allocation\n </div>\n )}\n <div className=\"min-w-0 space-y-2\">\n <div className=\"flex flex-wrap items-center gap-1.5\">\n <Badge variant=\"outline\">{p.baseCurrency}</Badge>\n <Badge variant=\"secondary\">Drift ≤ {formatPct(p.driftThresholdPct)}</Badge>\n </div>\n {donut.length > 0 ? (\n <ul className=\"space-y-1\" aria-label=\"Target allocation\">\n {donut.slice(0, 3).map((d) => (\n <li key={d.label} className=\"flex items-center gap-1.5 text-xs\">\n <span className=\"truncate text-text-secondary\">{d.label}</span>\n <span className=\"ml-auto font-medium tabular-nums text-text-primary\">\n {formatPct(d.value, 0)}\n </span>\n </li>\n ))}\n </ul>\n ) : null}\n {tags.length > 0 ? (\n <div className=\"flex flex-wrap gap-1\">\n {tags.slice(0, 3).map((t) => (\n <Badge key={t} variant=\"outline\" className=\"text-[10px]\">\n {t}\n </Badge>\n ))}\n </div>\n ) : null}\n </div>\n </div>\n <Button variant=\"outline\" size=\"sm\" className=\"w-full\" onClick={onView}>\n View holdings\n </Button>\n </CardContent>\n </Card>\n </FadeIn>\n );\n}\n\n// ============================================================================\n// Page\n// ============================================================================\n\nexport function PortfoliosPage() {\n useTrackBrainyRichPageView('brainyrich.portfolios.viewed');\n\n const {\n items: portfolios,\n hasMore,\n loadMore,\n isLoading,\n isFetchingNextPage,\n isError,\n refetch,\n } = usePagedPortfolios();\n const archivePortfolio = useArchivePortfolio();\n\n const [formOpen, setFormOpen] = useState(false);\n const [editingPortfolio, setEditingPortfolio] = useState<BraPortfolio | null>(null);\n const [detailId, setDetailId] = useState<string | null>(null);\n const [portfolioToArchive, setPortfolioToArchive] = useState<BraPortfolio | null>(null);\n\n const activeCount = portfolios.filter((p) => p.status === 'ACTIVE').length;\n const draftCount = portfolios.filter((p) => p.status === 'DRAFT').length;\n const avgDrift =\n portfolios.length > 0\n ? portfolios.reduce((s, p) => s + p.driftThresholdPct, 0) / portfolios.length\n : 0;\n\n const openCreate = () => {\n setEditingPortfolio(null);\n setFormOpen(true);\n };\n const openEdit = (p: BraPortfolio) => {\n setEditingPortfolio(p);\n setFormOpen(true);\n };\n\n return (\n <div data-tour=\"brainyrich-page-portfolios\" className=\"contents\">\n <SectionShell\n title=\"Portfolios\"\n description=\"Organize holdings into strategies and keep allocations on target.\"\n badges={portfolios.length > 0 ? [`${portfolios.length} total`] : []}\n actions={\n <Button size=\"sm\" onClick={openCreate}>\n <Plus className=\"size-4\" aria-hidden=\"true\" />\n New portfolio\n </Button>\n }\n >\n {isLoading ? (\n <LoadingState rows={4} />\n ) : isError ? (\n <ErrorState onRetry={() => refetch()} />\n ) : portfolios.length === 0 ? (\n <EmptyState\n icon={<Briefcase className=\"size-6\" aria-hidden=\"true\" />}\n title=\"No portfolios yet\"\n description=\"Create your first portfolio to start organizing holdings and tracking drift.\"\n action={\n <Button size=\"sm\" onClick={openCreate}>\n <Plus className=\"size-4\" aria-hidden=\"true\" />\n New portfolio\n </Button>\n }\n />\n ) : (\n <>\n <FadeIn className=\"grid grid-cols-2 gap-3 lg:grid-cols-4\">\n <StatTile label=\"Portfolios\" value={String(portfolios.length)} glow />\n <StatTile label=\"Active\" value={String(activeCount)} />\n <StatTile label=\"Drafts\" value={String(draftCount)} />\n <StatTile label=\"Avg drift threshold\" value={formatPct(avgDrift)} />\n </FadeIn>\n\n <div className=\"grid gap-4 md:grid-cols-2 xl:grid-cols-3\">\n {portfolios.map((p, i) => (\n <PortfolioCard\n key={p.id}\n portfolio={p}\n index={i}\n onView={() => setDetailId(p.id)}\n onEdit={() => openEdit(p)}\n onArchive={() => setPortfolioToArchive(p)}\n />\n ))}\n </div>\n\n <LoadMore hasMore={hasMore} onClick={loadMore} loading={isFetchingNextPage} />\n </>\n )}\n\n <PortfolioFormDialog\n open={formOpen}\n onOpenChange={setFormOpen}\n editing={editingPortfolio}\n />\n\n <PortfolioDetailSheet\n portfolioId={detailId}\n onOpenChange={(open) => {\n if (!open) setDetailId(null);\n }}\n />\n\n <ConfirmDialog\n open={portfolioToArchive !== null}\n onOpenChange={(open) => {\n if (!open) setPortfolioToArchive(null);\n }}\n title=\"Archive portfolio?\"\n description={\n portfolioToArchive\n ? `Archive “${portfolioToArchive.name}”. It will move to archived status and stop appearing in active views.`\n : undefined\n }\n confirmLabel=\"Archive\"\n pendingLabel=\"Archiving…\"\n destructive\n pending={archivePortfolio.isPending}\n onConfirm={() => {\n if (!portfolioToArchive) return;\n archivePortfolio.mutate(portfolioToArchive.id, {\n onSuccess: () => {\n hapticSuccess();\n void showToast('Portfolio archived');\n setPortfolioToArchive(null);\n },\n });\n }}\n />\n </SectionShell>\n </div>\n );\n}\n"],"mappings":"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"}
|