@burdenoff/microfe-brainyrich 2026.819.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.
Files changed (496) hide show
  1. package/dist/_virtual/_rolldown/runtime.js +1 -1
  2. package/dist/brainyrich/BrainyRichRoot.d.ts +0 -1
  3. package/dist/brainyrich/BrainyRichRoot.js.map +1 -1
  4. package/dist/brainyrich/BrainyRichRoutes.js.map +1 -1
  5. package/dist/brainyrich/components/ActorName.js.map +1 -1
  6. package/dist/brainyrich/components/ConfirmDialog.d.ts +0 -1
  7. package/dist/brainyrich/components/ConfirmDialog.js.map +1 -1
  8. package/dist/brainyrich/components/LoadMore.js.map +1 -1
  9. package/dist/brainyrich/components/notify.js.map +1 -1
  10. package/dist/brainyrich/components/pickers.js.map +1 -1
  11. package/dist/brainyrich/components/reuse/CommentThread.js.map +1 -1
  12. package/dist/brainyrich/components/reuse/DocumentsPanel.js.map +1 -1
  13. package/dist/brainyrich/components/reuse/TagField.js.map +1 -1
  14. package/dist/brainyrich/components/reuse/transport.js.map +1 -1
  15. package/dist/brainyrich/components/viz.d.ts +0 -1
  16. package/dist/brainyrich/components/viz.js.map +1 -1
  17. package/dist/brainyrich/hooks/useBrainyRichAnalytics.js.map +1 -1
  18. package/dist/brainyrich/hooks/useBrainyRichApi.d.ts +0 -1
  19. package/dist/brainyrich/hooks/useBrainyRichApi.js.map +1 -1
  20. package/dist/brainyrich/pages/AccountsPage.d.ts.map +1 -1
  21. package/dist/brainyrich/pages/AccountsPage.js +367 -363
  22. package/dist/brainyrich/pages/AccountsPage.js.map +1 -1
  23. package/dist/brainyrich/pages/AdminFirmPage.js.map +1 -1
  24. package/dist/brainyrich/pages/AiAssistantPage.js.map +1 -1
  25. package/dist/brainyrich/pages/AnalyticsPage.js.map +1 -1
  26. package/dist/brainyrich/pages/DashboardPage.js.map +1 -1
  27. package/dist/brainyrich/pages/FamilyPage.js.map +1 -1
  28. package/dist/brainyrich/pages/FirmPage.js.map +1 -1
  29. package/dist/brainyrich/pages/GoalsPage.js.map +1 -1
  30. package/dist/brainyrich/pages/InvestmentsPage.js.map +1 -1
  31. package/dist/brainyrich/pages/PortfoliosPage.js.map +1 -1
  32. package/dist/brainyrich/pages/ReportsPage.js.map +1 -1
  33. package/dist/brainyrich/pages/RiskPage.js.map +1 -1
  34. package/dist/brainyrich/pages/TaxPage.js.map +1 -1
  35. package/dist/brainyrich/pages/_shared.d.ts +0 -1
  36. package/dist/brainyrich/pages/_shared.js.map +1 -1
  37. package/dist/brainyrich/providers/BrainyRichProvider.d.ts +0 -1
  38. package/dist/brainyrich/providers/BrainyRichProvider.js.map +1 -1
  39. package/dist/brainyrich/services/createSandboxAssistantTransport.js.map +1 -1
  40. package/dist/brainyrich/types/bra.d.ts.map +1 -1
  41. package/dist/brainyrich/types/index.d.ts +0 -1
  42. package/dist/brainyrich/utils/cn.d.ts +0 -1
  43. package/dist/brainyrich/utils/i18n.d.ts +0 -1
  44. package/dist/brainyrich/utils/i18n.js.map +1 -1
  45. package/dist/brainyrich/utils/nativeControls.d.ts +0 -1
  46. package/dist/brainyrich/utils/nativeControls.js.map +1 -1
  47. package/dist/brainyrich/utils/navigation.js.map +1 -1
  48. package/dist/brainyrich/utils/pageContext.js.map +1 -1
  49. package/dist/dev/main.d.ts +0 -1
  50. package/dist/node_modules/clsx/dist/clsx.js.map +1 -1
  51. package/dist/node_modules/d3-array/src/ascending.js.map +1 -1
  52. package/dist/node_modules/d3-array/src/bisect.js.map +1 -1
  53. package/dist/node_modules/d3-array/src/bisector.js.map +1 -1
  54. package/dist/node_modules/d3-array/src/descending.js.map +1 -1
  55. package/dist/node_modules/d3-array/src/max.js.map +1 -1
  56. package/dist/node_modules/d3-array/src/min.js.map +1 -1
  57. package/dist/node_modules/d3-array/src/number.js.map +1 -1
  58. package/dist/node_modules/d3-array/src/quantile.js.map +1 -1
  59. package/dist/node_modules/d3-array/src/quickselect.js.map +1 -1
  60. package/dist/node_modules/d3-array/src/range.js.map +1 -1
  61. package/dist/node_modules/d3-array/src/sort.js.map +1 -1
  62. package/dist/node_modules/d3-array/src/ticks.js.map +1 -1
  63. package/dist/node_modules/d3-color/src/color.js.map +1 -1
  64. package/dist/node_modules/d3-color/src/define.js.map +1 -1
  65. package/dist/node_modules/d3-format/src/defaultLocale.js.map +1 -1
  66. package/dist/node_modules/d3-format/src/exponent.js.map +1 -1
  67. package/dist/node_modules/d3-format/src/formatDecimal.js.map +1 -1
  68. package/dist/node_modules/d3-format/src/formatGroup.js.map +1 -1
  69. package/dist/node_modules/d3-format/src/formatNumerals.js.map +1 -1
  70. package/dist/node_modules/d3-format/src/formatPrefixAuto.js.map +1 -1
  71. package/dist/node_modules/d3-format/src/formatRounded.js.map +1 -1
  72. package/dist/node_modules/d3-format/src/formatSpecifier.js.map +1 -1
  73. package/dist/node_modules/d3-format/src/formatTrim.js.map +1 -1
  74. package/dist/node_modules/d3-format/src/formatTypes.js.map +1 -1
  75. package/dist/node_modules/d3-format/src/identity.js.map +1 -1
  76. package/dist/node_modules/d3-format/src/locale.js.map +1 -1
  77. package/dist/node_modules/d3-format/src/precisionFixed.js.map +1 -1
  78. package/dist/node_modules/d3-format/src/precisionPrefix.js.map +1 -1
  79. package/dist/node_modules/d3-format/src/precisionRound.js.map +1 -1
  80. package/dist/node_modules/d3-interpolate/src/array.js.map +1 -1
  81. package/dist/node_modules/d3-interpolate/src/basis.js.map +1 -1
  82. package/dist/node_modules/d3-interpolate/src/basisClosed.js.map +1 -1
  83. package/dist/node_modules/d3-interpolate/src/color.js.map +1 -1
  84. package/dist/node_modules/d3-interpolate/src/date.js.map +1 -1
  85. package/dist/node_modules/d3-interpolate/src/number.js.map +1 -1
  86. package/dist/node_modules/d3-interpolate/src/numberArray.js.map +1 -1
  87. package/dist/node_modules/d3-interpolate/src/object.js.map +1 -1
  88. package/dist/node_modules/d3-interpolate/src/piecewise.js.map +1 -1
  89. package/dist/node_modules/d3-interpolate/src/rgb.js.map +1 -1
  90. package/dist/node_modules/d3-interpolate/src/round.js.map +1 -1
  91. package/dist/node_modules/d3-interpolate/src/string.js.map +1 -1
  92. package/dist/node_modules/d3-interpolate/src/value.js.map +1 -1
  93. package/dist/node_modules/d3-path/src/path.js.map +1 -1
  94. package/dist/node_modules/d3-scale/src/band.js.map +1 -1
  95. package/dist/node_modules/d3-scale/src/constant.js.map +1 -1
  96. package/dist/node_modules/d3-scale/src/continuous.js.map +1 -1
  97. package/dist/node_modules/d3-scale/src/diverging.js.map +1 -1
  98. package/dist/node_modules/d3-scale/src/identity.js.map +1 -1
  99. package/dist/node_modules/d3-scale/src/init.js.map +1 -1
  100. package/dist/node_modules/d3-scale/src/linear.js.map +1 -1
  101. package/dist/node_modules/d3-scale/src/log.js.map +1 -1
  102. package/dist/node_modules/d3-scale/src/nice.js.map +1 -1
  103. package/dist/node_modules/d3-scale/src/number.js.map +1 -1
  104. package/dist/node_modules/d3-scale/src/ordinal.js.map +1 -1
  105. package/dist/node_modules/d3-scale/src/pow.js.map +1 -1
  106. package/dist/node_modules/d3-scale/src/quantile.js.map +1 -1
  107. package/dist/node_modules/d3-scale/src/quantize.js.map +1 -1
  108. package/dist/node_modules/d3-scale/src/radial.js.map +1 -1
  109. package/dist/node_modules/d3-scale/src/sequential.js.map +1 -1
  110. package/dist/node_modules/d3-scale/src/sequentialQuantile.js.map +1 -1
  111. package/dist/node_modules/d3-scale/src/symlog.js.map +1 -1
  112. package/dist/node_modules/d3-scale/src/threshold.js.map +1 -1
  113. package/dist/node_modules/d3-scale/src/tickFormat.js.map +1 -1
  114. package/dist/node_modules/d3-scale/src/time.js.map +1 -1
  115. package/dist/node_modules/d3-scale/src/utcTime.js.map +1 -1
  116. package/dist/node_modules/d3-shape/src/area.js.map +1 -1
  117. package/dist/node_modules/d3-shape/src/array.js.map +1 -1
  118. package/dist/node_modules/d3-shape/src/constant.js.map +1 -1
  119. package/dist/node_modules/d3-shape/src/curve/basis.js.map +1 -1
  120. package/dist/node_modules/d3-shape/src/curve/basisClosed.js.map +1 -1
  121. package/dist/node_modules/d3-shape/src/curve/basisOpen.js.map +1 -1
  122. package/dist/node_modules/d3-shape/src/curve/bump.js.map +1 -1
  123. package/dist/node_modules/d3-shape/src/curve/linear.js.map +1 -1
  124. package/dist/node_modules/d3-shape/src/curve/linearClosed.js.map +1 -1
  125. package/dist/node_modules/d3-shape/src/curve/monotone.js.map +1 -1
  126. package/dist/node_modules/d3-shape/src/curve/natural.js.map +1 -1
  127. package/dist/node_modules/d3-shape/src/curve/step.js.map +1 -1
  128. package/dist/node_modules/d3-shape/src/line.js.map +1 -1
  129. package/dist/node_modules/d3-shape/src/noop.js.map +1 -1
  130. package/dist/node_modules/d3-shape/src/offset/expand.js.map +1 -1
  131. package/dist/node_modules/d3-shape/src/offset/none.js.map +1 -1
  132. package/dist/node_modules/d3-shape/src/offset/silhouette.js.map +1 -1
  133. package/dist/node_modules/d3-shape/src/offset/wiggle.js.map +1 -1
  134. package/dist/node_modules/d3-shape/src/order/none.js.map +1 -1
  135. package/dist/node_modules/d3-shape/src/path.js.map +1 -1
  136. package/dist/node_modules/d3-shape/src/point.js.map +1 -1
  137. package/dist/node_modules/d3-shape/src/stack.js.map +1 -1
  138. package/dist/node_modules/d3-shape/src/symbol/circle.js.map +1 -1
  139. package/dist/node_modules/d3-shape/src/symbol/cross.js.map +1 -1
  140. package/dist/node_modules/d3-shape/src/symbol/diamond.js.map +1 -1
  141. package/dist/node_modules/d3-shape/src/symbol/square.js.map +1 -1
  142. package/dist/node_modules/d3-shape/src/symbol/star.js.map +1 -1
  143. package/dist/node_modules/d3-shape/src/symbol/triangle.js.map +1 -1
  144. package/dist/node_modules/d3-shape/src/symbol/wye.js.map +1 -1
  145. package/dist/node_modules/d3-shape/src/symbol.js.map +1 -1
  146. package/dist/node_modules/d3-time/src/day.js.map +1 -1
  147. package/dist/node_modules/d3-time/src/hour.js.map +1 -1
  148. package/dist/node_modules/d3-time/src/interval.js.map +1 -1
  149. package/dist/node_modules/d3-time/src/millisecond.js.map +1 -1
  150. package/dist/node_modules/d3-time/src/minute.js.map +1 -1
  151. package/dist/node_modules/d3-time/src/month.js.map +1 -1
  152. package/dist/node_modules/d3-time/src/second.js.map +1 -1
  153. package/dist/node_modules/d3-time/src/ticks.js.map +1 -1
  154. package/dist/node_modules/d3-time/src/week.js.map +1 -1
  155. package/dist/node_modules/d3-time/src/year.js.map +1 -1
  156. package/dist/node_modules/d3-time-format/src/defaultLocale.js.map +1 -1
  157. package/dist/node_modules/d3-time-format/src/locale.js.map +1 -1
  158. package/dist/node_modules/decimal.js-light/decimal.js.map +1 -1
  159. package/dist/node_modules/eventemitter3/index.js.map +1 -1
  160. package/dist/node_modules/fast-equals/dist/es/index.js.map +1 -1
  161. package/dist/node_modules/internmap/src/index.js.map +1 -1
  162. package/dist/node_modules/lodash/_DataView.js.map +1 -1
  163. package/dist/node_modules/lodash/_Hash.js.map +1 -1
  164. package/dist/node_modules/lodash/_ListCache.js.map +1 -1
  165. package/dist/node_modules/lodash/_Map.js.map +1 -1
  166. package/dist/node_modules/lodash/_MapCache.js.map +1 -1
  167. package/dist/node_modules/lodash/_Promise.js.map +1 -1
  168. package/dist/node_modules/lodash/_Set.js.map +1 -1
  169. package/dist/node_modules/lodash/_SetCache.js.map +1 -1
  170. package/dist/node_modules/lodash/_Stack.js +2 -1
  171. package/dist/node_modules/lodash/_Stack.js.map +1 -1
  172. package/dist/node_modules/lodash/_Symbol.js.map +1 -1
  173. package/dist/node_modules/lodash/_Uint8Array.js.map +1 -1
  174. package/dist/node_modules/lodash/_WeakMap.js.map +1 -1
  175. package/dist/node_modules/lodash/_apply.js.map +1 -1
  176. package/dist/node_modules/lodash/_arrayEvery.js.map +1 -1
  177. package/dist/node_modules/lodash/_arrayFilter.js.map +1 -1
  178. package/dist/node_modules/lodash/_arrayIncludes.js.map +1 -1
  179. package/dist/node_modules/lodash/_arrayIncludesWith.js.map +1 -1
  180. package/dist/node_modules/lodash/_arrayLikeKeys.js.map +1 -1
  181. package/dist/node_modules/lodash/_arrayMap.js.map +1 -1
  182. package/dist/node_modules/lodash/_arrayPush.js.map +1 -1
  183. package/dist/node_modules/lodash/_arraySome.js.map +1 -1
  184. package/dist/node_modules/lodash/_asciiToArray.js.map +1 -1
  185. package/dist/node_modules/lodash/_assocIndexOf.js.map +1 -1
  186. package/dist/node_modules/lodash/_baseAssignValue.js.map +1 -1
  187. package/dist/node_modules/lodash/_baseEach.js.map +1 -1
  188. package/dist/node_modules/lodash/_baseEvery.js.map +1 -1
  189. package/dist/node_modules/lodash/_baseExtremum.js.map +1 -1
  190. package/dist/node_modules/lodash/_baseFindIndex.js.map +1 -1
  191. package/dist/node_modules/lodash/_baseFlatten.js.map +1 -1
  192. package/dist/node_modules/lodash/_baseFor.js.map +1 -1
  193. package/dist/node_modules/lodash/_baseForOwn.js.map +1 -1
  194. package/dist/node_modules/lodash/_baseGet.js.map +1 -1
  195. package/dist/node_modules/lodash/_baseGetAllKeys.js.map +1 -1
  196. package/dist/node_modules/lodash/_baseGetTag.js.map +1 -1
  197. package/dist/node_modules/lodash/_baseGt.js.map +1 -1
  198. package/dist/node_modules/lodash/_baseHasIn.js.map +1 -1
  199. package/dist/node_modules/lodash/_baseIndexOf.js.map +1 -1
  200. package/dist/node_modules/lodash/_baseIsArguments.js.map +1 -1
  201. package/dist/node_modules/lodash/_baseIsEqual.js.map +1 -1
  202. package/dist/node_modules/lodash/_baseIsEqualDeep.js.map +1 -1
  203. package/dist/node_modules/lodash/_baseIsMatch.js.map +1 -1
  204. package/dist/node_modules/lodash/_baseIsNaN.js.map +1 -1
  205. package/dist/node_modules/lodash/_baseIsNative.js.map +1 -1
  206. package/dist/node_modules/lodash/_baseIsTypedArray.js.map +1 -1
  207. package/dist/node_modules/lodash/_baseIteratee.js.map +1 -1
  208. package/dist/node_modules/lodash/_baseKeys.js.map +1 -1
  209. package/dist/node_modules/lodash/_baseLt.js.map +1 -1
  210. package/dist/node_modules/lodash/_baseMap.js.map +1 -1
  211. package/dist/node_modules/lodash/_baseMatches.js.map +1 -1
  212. package/dist/node_modules/lodash/_baseMatchesProperty.js.map +1 -1
  213. package/dist/node_modules/lodash/_baseOrderBy.js.map +1 -1
  214. package/dist/node_modules/lodash/_baseProperty.js.map +1 -1
  215. package/dist/node_modules/lodash/_basePropertyDeep.js.map +1 -1
  216. package/dist/node_modules/lodash/_baseRange.js.map +1 -1
  217. package/dist/node_modules/lodash/_baseRest.js.map +1 -1
  218. package/dist/node_modules/lodash/_baseSetToString.js.map +1 -1
  219. package/dist/node_modules/lodash/_baseSlice.js.map +1 -1
  220. package/dist/node_modules/lodash/_baseSome.js.map +1 -1
  221. package/dist/node_modules/lodash/_baseSortBy.js.map +1 -1
  222. package/dist/node_modules/lodash/_baseTimes.js.map +1 -1
  223. package/dist/node_modules/lodash/_baseToString.js.map +1 -1
  224. package/dist/node_modules/lodash/_baseTrim.js.map +1 -1
  225. package/dist/node_modules/lodash/_baseUnary.js.map +1 -1
  226. package/dist/node_modules/lodash/_baseUniq.js.map +1 -1
  227. package/dist/node_modules/lodash/_cacheHas.js.map +1 -1
  228. package/dist/node_modules/lodash/_castPath.js.map +1 -1
  229. package/dist/node_modules/lodash/_castSlice.js.map +1 -1
  230. package/dist/node_modules/lodash/_compareAscending.js.map +1 -1
  231. package/dist/node_modules/lodash/_compareMultiple.js.map +1 -1
  232. package/dist/node_modules/lodash/_coreJsData.js.map +1 -1
  233. package/dist/node_modules/lodash/_createBaseEach.js.map +1 -1
  234. package/dist/node_modules/lodash/_createBaseFor.js.map +1 -1
  235. package/dist/node_modules/lodash/_createCaseFirst.js.map +1 -1
  236. package/dist/node_modules/lodash/_createFind.js.map +1 -1
  237. package/dist/node_modules/lodash/_createRange.js.map +1 -1
  238. package/dist/node_modules/lodash/_createSet.js.map +1 -1
  239. package/dist/node_modules/lodash/_defineProperty.js.map +1 -1
  240. package/dist/node_modules/lodash/_equalArrays.js.map +1 -1
  241. package/dist/node_modules/lodash/_equalByTag.js.map +1 -1
  242. package/dist/node_modules/lodash/_equalObjects.js.map +1 -1
  243. package/dist/node_modules/lodash/_freeGlobal.js.map +1 -1
  244. package/dist/node_modules/lodash/_getAllKeys.js.map +1 -1
  245. package/dist/node_modules/lodash/_getMapData.js.map +1 -1
  246. package/dist/node_modules/lodash/_getMatchData.js.map +1 -1
  247. package/dist/node_modules/lodash/_getNative.js.map +1 -1
  248. package/dist/node_modules/lodash/_getPrototype.js.map +1 -1
  249. package/dist/node_modules/lodash/_getRawTag.js.map +1 -1
  250. package/dist/node_modules/lodash/_getSymbols.js.map +1 -1
  251. package/dist/node_modules/lodash/_getTag.js.map +1 -1
  252. package/dist/node_modules/lodash/_getValue.js.map +1 -1
  253. package/dist/node_modules/lodash/_hasPath.js.map +1 -1
  254. package/dist/node_modules/lodash/_hasUnicode.js.map +1 -1
  255. package/dist/node_modules/lodash/_hashClear.js.map +1 -1
  256. package/dist/node_modules/lodash/_hashDelete.js.map +1 -1
  257. package/dist/node_modules/lodash/_hashGet.js.map +1 -1
  258. package/dist/node_modules/lodash/_hashHas.js.map +1 -1
  259. package/dist/node_modules/lodash/_hashSet.js.map +1 -1
  260. package/dist/node_modules/lodash/_isFlattenable.js.map +1 -1
  261. package/dist/node_modules/lodash/_isIndex.js.map +1 -1
  262. package/dist/node_modules/lodash/_isIterateeCall.js.map +1 -1
  263. package/dist/node_modules/lodash/_isKey.js.map +1 -1
  264. package/dist/node_modules/lodash/_isKeyable.js.map +1 -1
  265. package/dist/node_modules/lodash/_isMasked.js.map +1 -1
  266. package/dist/node_modules/lodash/_isPrototype.js.map +1 -1
  267. package/dist/node_modules/lodash/_isStrictComparable.js.map +1 -1
  268. package/dist/node_modules/lodash/_listCacheClear.js.map +1 -1
  269. package/dist/node_modules/lodash/_listCacheDelete.js.map +1 -1
  270. package/dist/node_modules/lodash/_listCacheGet.js.map +1 -1
  271. package/dist/node_modules/lodash/_listCacheHas.js.map +1 -1
  272. package/dist/node_modules/lodash/_listCacheSet.js.map +1 -1
  273. package/dist/node_modules/lodash/_mapCacheClear.js.map +1 -1
  274. package/dist/node_modules/lodash/_mapCacheDelete.js.map +1 -1
  275. package/dist/node_modules/lodash/_mapCacheGet.js.map +1 -1
  276. package/dist/node_modules/lodash/_mapCacheHas.js.map +1 -1
  277. package/dist/node_modules/lodash/_mapCacheSet.js.map +1 -1
  278. package/dist/node_modules/lodash/_mapToArray.js.map +1 -1
  279. package/dist/node_modules/lodash/_matchesStrictComparable.js.map +1 -1
  280. package/dist/node_modules/lodash/_memoizeCapped.js.map +1 -1
  281. package/dist/node_modules/lodash/_nativeCreate.js.map +1 -1
  282. package/dist/node_modules/lodash/_nativeKeys.js.map +1 -1
  283. package/dist/node_modules/lodash/_nodeUtil.js.map +1 -1
  284. package/dist/node_modules/lodash/_objectToString.js.map +1 -1
  285. package/dist/node_modules/lodash/_overArg.js.map +1 -1
  286. package/dist/node_modules/lodash/_overRest.js.map +1 -1
  287. package/dist/node_modules/lodash/_root.js.map +1 -1
  288. package/dist/node_modules/lodash/_setCacheAdd.js.map +1 -1
  289. package/dist/node_modules/lodash/_setCacheHas.js.map +1 -1
  290. package/dist/node_modules/lodash/_setToArray.js.map +1 -1
  291. package/dist/node_modules/lodash/_setToString.js.map +1 -1
  292. package/dist/node_modules/lodash/_shortOut.js.map +1 -1
  293. package/dist/node_modules/lodash/_stackClear.js.map +1 -1
  294. package/dist/node_modules/lodash/_stackDelete.js.map +1 -1
  295. package/dist/node_modules/lodash/_stackGet.js.map +1 -1
  296. package/dist/node_modules/lodash/_stackHas.js.map +1 -1
  297. package/dist/node_modules/lodash/_stackSet.js.map +1 -1
  298. package/dist/node_modules/lodash/_strictIndexOf.js.map +1 -1
  299. package/dist/node_modules/lodash/_stringToArray.js.map +1 -1
  300. package/dist/node_modules/lodash/_stringToPath.js.map +1 -1
  301. package/dist/node_modules/lodash/_toKey.js.map +1 -1
  302. package/dist/node_modules/lodash/_toSource.js.map +1 -1
  303. package/dist/node_modules/lodash/_trimmedEndIndex.js.map +1 -1
  304. package/dist/node_modules/lodash/_unicodeToArray.js.map +1 -1
  305. package/dist/node_modules/lodash/constant.js.map +1 -1
  306. package/dist/node_modules/lodash/debounce.js.map +1 -1
  307. package/dist/node_modules/lodash/eq.js.map +1 -1
  308. package/dist/node_modules/lodash/every.js.map +1 -1
  309. package/dist/node_modules/lodash/find.js.map +1 -1
  310. package/dist/node_modules/lodash/findIndex.js.map +1 -1
  311. package/dist/node_modules/lodash/flatMap.js.map +1 -1
  312. package/dist/node_modules/lodash/get.js.map +1 -1
  313. package/dist/node_modules/lodash/hasIn.js.map +1 -1
  314. package/dist/node_modules/lodash/identity.js.map +1 -1
  315. package/dist/node_modules/lodash/isArguments.js.map +1 -1
  316. package/dist/node_modules/lodash/isArray.js.map +1 -1
  317. package/dist/node_modules/lodash/isArrayLike.js.map +1 -1
  318. package/dist/node_modules/lodash/isBoolean.js.map +1 -1
  319. package/dist/node_modules/lodash/isBuffer.js.map +1 -1
  320. package/dist/node_modules/lodash/isEqual.js.map +1 -1
  321. package/dist/node_modules/lodash/isFunction.js.map +1 -1
  322. package/dist/node_modules/lodash/isLength.js.map +1 -1
  323. package/dist/node_modules/lodash/isNaN.js.map +1 -1
  324. package/dist/node_modules/lodash/isNil.js.map +1 -1
  325. package/dist/node_modules/lodash/isNumber.js.map +1 -1
  326. package/dist/node_modules/lodash/isObject.js.map +1 -1
  327. package/dist/node_modules/lodash/isObjectLike.js.map +1 -1
  328. package/dist/node_modules/lodash/isPlainObject.js.map +1 -1
  329. package/dist/node_modules/lodash/isString.js.map +1 -1
  330. package/dist/node_modules/lodash/isSymbol.js.map +1 -1
  331. package/dist/node_modules/lodash/isTypedArray.js.map +1 -1
  332. package/dist/node_modules/lodash/keys.js.map +1 -1
  333. package/dist/node_modules/lodash/last.js.map +1 -1
  334. package/dist/node_modules/lodash/map.js.map +1 -1
  335. package/dist/node_modules/lodash/mapValues.js.map +1 -1
  336. package/dist/node_modules/lodash/max.js.map +1 -1
  337. package/dist/node_modules/lodash/maxBy.js.map +1 -1
  338. package/dist/node_modules/lodash/memoize.js.map +1 -1
  339. package/dist/node_modules/lodash/min.js.map +1 -1
  340. package/dist/node_modules/lodash/minBy.js.map +1 -1
  341. package/dist/node_modules/lodash/noop.js.map +1 -1
  342. package/dist/node_modules/lodash/now.js.map +1 -1
  343. package/dist/node_modules/lodash/property.js.map +1 -1
  344. package/dist/node_modules/lodash/range.js.map +1 -1
  345. package/dist/node_modules/lodash/some.js.map +1 -1
  346. package/dist/node_modules/lodash/sortBy.js.map +1 -1
  347. package/dist/node_modules/lodash/stubArray.js.map +1 -1
  348. package/dist/node_modules/lodash/stubFalse.js.map +1 -1
  349. package/dist/node_modules/lodash/throttle.js.map +1 -1
  350. package/dist/node_modules/lodash/toFinite.js.map +1 -1
  351. package/dist/node_modules/lodash/toInteger.js.map +1 -1
  352. package/dist/node_modules/lodash/toNumber.js.map +1 -1
  353. package/dist/node_modules/lodash/toString.js.map +1 -1
  354. package/dist/node_modules/lodash/uniqBy.js.map +1 -1
  355. package/dist/node_modules/lodash/upperFirst.js.map +1 -1
  356. package/dist/node_modules/object-assign/index.js.map +1 -1
  357. package/dist/node_modules/prop-types/checkPropTypes.js.map +1 -1
  358. package/dist/node_modules/prop-types/factoryWithThrowingShims.js.map +1 -1
  359. package/dist/node_modules/prop-types/factoryWithTypeCheckers.js.map +1 -1
  360. package/dist/node_modules/prop-types/index.js.map +1 -1
  361. package/dist/node_modules/prop-types/lib/ReactPropTypesSecret.js.map +1 -1
  362. package/dist/node_modules/prop-types/lib/has.js.map +1 -1
  363. package/dist/node_modules/prop-types/node_modules/react-is/cjs/react-is.development.js.map +1 -1
  364. package/dist/node_modules/prop-types/node_modules/react-is/cjs/react-is.production.min.js.map +1 -1
  365. package/dist/node_modules/prop-types/node_modules/react-is/index.js.map +1 -1
  366. package/dist/node_modules/react-is/cjs/react-is.development.js.map +1 -1
  367. package/dist/node_modules/react-is/cjs/react-is.production.min.js.map +1 -1
  368. package/dist/node_modules/react-is/index.js.map +1 -1
  369. package/dist/node_modules/react-smooth/es6/Animate.js +2 -2
  370. package/dist/node_modules/react-smooth/es6/Animate.js.map +1 -1
  371. package/dist/node_modules/react-smooth/es6/AnimateManager.js.map +1 -1
  372. package/dist/node_modules/react-smooth/es6/configUpdate.js.map +1 -1
  373. package/dist/node_modules/react-smooth/es6/easing.js.map +1 -1
  374. package/dist/node_modules/react-smooth/es6/setRafTimeout.js.map +1 -1
  375. package/dist/node_modules/react-smooth/es6/util.js.map +1 -1
  376. package/dist/node_modules/recharts/es6/cartesian/Area.js +13 -13
  377. package/dist/node_modules/recharts/es6/cartesian/Area.js.map +1 -1
  378. package/dist/node_modules/recharts/es6/cartesian/Bar.js +9 -9
  379. package/dist/node_modules/recharts/es6/cartesian/Bar.js.map +1 -1
  380. package/dist/node_modules/recharts/es6/cartesian/Brush.js +12 -12
  381. package/dist/node_modules/recharts/es6/cartesian/Brush.js.map +1 -1
  382. package/dist/node_modules/recharts/es6/cartesian/CartesianAxis.js +7 -7
  383. package/dist/node_modules/recharts/es6/cartesian/CartesianAxis.js.map +1 -1
  384. package/dist/node_modules/recharts/es6/cartesian/ErrorBar.js +4 -4
  385. package/dist/node_modules/recharts/es6/cartesian/ErrorBar.js.map +1 -1
  386. package/dist/node_modules/recharts/es6/cartesian/ReferenceArea.js +3 -3
  387. package/dist/node_modules/recharts/es6/cartesian/ReferenceArea.js.map +1 -1
  388. package/dist/node_modules/recharts/es6/cartesian/ReferenceDot.js +3 -3
  389. package/dist/node_modules/recharts/es6/cartesian/ReferenceDot.js.map +1 -1
  390. package/dist/node_modules/recharts/es6/cartesian/ReferenceLine.js +4 -4
  391. package/dist/node_modules/recharts/es6/cartesian/ReferenceLine.js.map +1 -1
  392. package/dist/node_modules/recharts/es6/cartesian/XAxis.js +3 -3
  393. package/dist/node_modules/recharts/es6/cartesian/XAxis.js.map +1 -1
  394. package/dist/node_modules/recharts/es6/cartesian/YAxis.js +3 -3
  395. package/dist/node_modules/recharts/es6/cartesian/YAxis.js.map +1 -1
  396. package/dist/node_modules/recharts/es6/cartesian/getEquidistantTicks.js.map +1 -1
  397. package/dist/node_modules/recharts/es6/cartesian/getTicks.js.map +1 -1
  398. package/dist/node_modules/recharts/es6/chart/AccessibilityManager.js +1 -1
  399. package/dist/node_modules/recharts/es6/chart/AccessibilityManager.js.map +1 -1
  400. package/dist/node_modules/recharts/es6/chart/AreaChart.js.map +1 -1
  401. package/dist/node_modules/recharts/es6/chart/BarChart.js.map +1 -1
  402. package/dist/node_modules/recharts/es6/chart/PieChart.js.map +1 -1
  403. package/dist/node_modules/recharts/es6/chart/generateCategoricalChart.js +22 -22
  404. package/dist/node_modules/recharts/es6/chart/generateCategoricalChart.js.map +1 -1
  405. package/dist/node_modules/recharts/es6/component/Cell.js.map +1 -1
  406. package/dist/node_modules/recharts/es6/component/Cursor.js +1 -1
  407. package/dist/node_modules/recharts/es6/component/Cursor.js.map +1 -1
  408. package/dist/node_modules/recharts/es6/component/DefaultLegendContent.js +11 -11
  409. package/dist/node_modules/recharts/es6/component/DefaultLegendContent.js.map +1 -1
  410. package/dist/node_modules/recharts/es6/component/DefaultTooltipContent.js +6 -6
  411. package/dist/node_modules/recharts/es6/component/DefaultTooltipContent.js.map +1 -1
  412. package/dist/node_modules/recharts/es6/component/Label.js +14 -14
  413. package/dist/node_modules/recharts/es6/component/Label.js.map +1 -1
  414. package/dist/node_modules/recharts/es6/component/LabelList.js +6 -6
  415. package/dist/node_modules/recharts/es6/component/LabelList.js.map +1 -1
  416. package/dist/node_modules/recharts/es6/component/Legend.js +5 -5
  417. package/dist/node_modules/recharts/es6/component/Legend.js.map +1 -1
  418. package/dist/node_modules/recharts/es6/component/ResponsiveContainer.js +3 -3
  419. package/dist/node_modules/recharts/es6/component/ResponsiveContainer.js.map +1 -1
  420. package/dist/node_modules/recharts/es6/component/Text.js +2 -2
  421. package/dist/node_modules/recharts/es6/component/Text.js.map +1 -1
  422. package/dist/node_modules/recharts/es6/component/Tooltip.js +3 -3
  423. package/dist/node_modules/recharts/es6/component/Tooltip.js.map +1 -1
  424. package/dist/node_modules/recharts/es6/component/TooltipBoundingBox.js +2 -2
  425. package/dist/node_modules/recharts/es6/component/TooltipBoundingBox.js.map +1 -1
  426. package/dist/node_modules/recharts/es6/container/Layer.js +2 -2
  427. package/dist/node_modules/recharts/es6/container/Layer.js.map +1 -1
  428. package/dist/node_modules/recharts/es6/container/Surface.js +2 -2
  429. package/dist/node_modules/recharts/es6/container/Surface.js.map +1 -1
  430. package/dist/node_modules/recharts/es6/context/chartLayoutContext.js +2 -2
  431. package/dist/node_modules/recharts/es6/context/chartLayoutContext.js.map +1 -1
  432. package/dist/node_modules/recharts/es6/polar/Pie.js +13 -13
  433. package/dist/node_modules/recharts/es6/polar/Pie.js.map +1 -1
  434. package/dist/node_modules/recharts/es6/polar/PolarAngleAxis.js +8 -8
  435. package/dist/node_modules/recharts/es6/polar/PolarAngleAxis.js.map +1 -1
  436. package/dist/node_modules/recharts/es6/polar/PolarRadiusAxis.js +6 -6
  437. package/dist/node_modules/recharts/es6/polar/PolarRadiusAxis.js.map +1 -1
  438. package/dist/node_modules/recharts/es6/shape/Cross.js +1 -1
  439. package/dist/node_modules/recharts/es6/shape/Cross.js.map +1 -1
  440. package/dist/node_modules/recharts/es6/shape/Curve.js +1 -1
  441. package/dist/node_modules/recharts/es6/shape/Curve.js.map +1 -1
  442. package/dist/node_modules/recharts/es6/shape/Dot.js +1 -1
  443. package/dist/node_modules/recharts/es6/shape/Dot.js.map +1 -1
  444. package/dist/node_modules/recharts/es6/shape/Polygon.js +4 -4
  445. package/dist/node_modules/recharts/es6/shape/Polygon.js.map +1 -1
  446. package/dist/node_modules/recharts/es6/shape/Rectangle.js +4 -4
  447. package/dist/node_modules/recharts/es6/shape/Rectangle.js.map +1 -1
  448. package/dist/node_modules/recharts/es6/shape/Sector.js +1 -1
  449. package/dist/node_modules/recharts/es6/shape/Sector.js.map +1 -1
  450. package/dist/node_modules/recharts/es6/shape/Symbols.js +1 -1
  451. package/dist/node_modules/recharts/es6/shape/Symbols.js.map +1 -1
  452. package/dist/node_modules/recharts/es6/shape/Trapezoid.js +4 -4
  453. package/dist/node_modules/recharts/es6/shape/Trapezoid.js.map +1 -1
  454. package/dist/node_modules/recharts/es6/util/ActiveShapeUtils.js +9 -9
  455. package/dist/node_modules/recharts/es6/util/ActiveShapeUtils.js.map +1 -1
  456. package/dist/node_modules/recharts/es6/util/BarUtils.js +1 -1
  457. package/dist/node_modules/recharts/es6/util/BarUtils.js.map +1 -1
  458. package/dist/node_modules/recharts/es6/util/CartesianUtils.js +1 -1
  459. package/dist/node_modules/recharts/es6/util/CartesianUtils.js.map +1 -1
  460. package/dist/node_modules/recharts/es6/util/ChartUtils.js.map +1 -1
  461. package/dist/node_modules/recharts/es6/util/CssPrefixUtils.js.map +1 -1
  462. package/dist/node_modules/recharts/es6/util/DOMUtils.js.map +1 -1
  463. package/dist/node_modules/recharts/es6/util/DataUtils.js.map +1 -1
  464. package/dist/node_modules/recharts/es6/util/DetectReferenceElementsDomain.js.map +1 -1
  465. package/dist/node_modules/recharts/es6/util/Events.js.map +1 -1
  466. package/dist/node_modules/recharts/es6/util/Global.js.map +1 -1
  467. package/dist/node_modules/recharts/es6/util/IfOverflowMatches.js.map +1 -1
  468. package/dist/node_modules/recharts/es6/util/LogUtils.js.map +1 -1
  469. package/dist/node_modules/recharts/es6/util/PolarUtils.js +1 -1
  470. package/dist/node_modules/recharts/es6/util/PolarUtils.js.map +1 -1
  471. package/dist/node_modules/recharts/es6/util/ReactUtils.js +1 -1
  472. package/dist/node_modules/recharts/es6/util/ReactUtils.js.map +1 -1
  473. package/dist/node_modules/recharts/es6/util/ReduceCSSCalc.js +1 -1
  474. package/dist/node_modules/recharts/es6/util/ReduceCSSCalc.js.map +1 -1
  475. package/dist/node_modules/recharts/es6/util/ShallowEqual.js.map +1 -1
  476. package/dist/node_modules/recharts/es6/util/TickUtils.js.map +1 -1
  477. package/dist/node_modules/recharts/es6/util/calculateViewBox.js.map +1 -1
  478. package/dist/node_modules/recharts/es6/util/cursor/getCursorPoints.js.map +1 -1
  479. package/dist/node_modules/recharts/es6/util/cursor/getCursorRectangle.js.map +1 -1
  480. package/dist/node_modules/recharts/es6/util/cursor/getRadialCursorPoints.js.map +1 -1
  481. package/dist/node_modules/recharts/es6/util/getEveryNthWithCondition.js.map +1 -1
  482. package/dist/node_modules/recharts/es6/util/getLegendProps.js.map +1 -1
  483. package/dist/node_modules/recharts/es6/util/isDomainSpecifiedByUser.js.map +1 -1
  484. package/dist/node_modules/recharts/es6/util/payload/getUniqPayload.js.map +1 -1
  485. package/dist/node_modules/recharts/es6/util/tooltip/translate.js.map +1 -1
  486. package/dist/node_modules/recharts/es6/util/types.js +1 -1
  487. package/dist/node_modules/recharts/es6/util/types.js.map +1 -1
  488. package/dist/node_modules/recharts-scale/es6/getNiceTickValues.js.map +1 -1
  489. package/dist/node_modules/recharts-scale/es6/util/arithmetic.js.map +1 -1
  490. package/dist/node_modules/recharts-scale/es6/util/utils.js.map +1 -1
  491. package/dist/node_modules/sonner/dist/index.js.map +1 -1
  492. package/dist/node_modules/tailwind-merge/dist/bundle-mjs.js +1 -1
  493. package/dist/node_modules/tailwind-merge/dist/bundle-mjs.js.map +1 -1
  494. package/dist/node_modules/tiny-invariant/dist/esm/tiny-invariant.js.map +1 -1
  495. package/dist/stories/LoadMore.stories.d.ts +0 -1
  496. package/package.json +15 -7
@@ -1,5 +1,5 @@
1
1
  //#region \0rolldown/runtime.js
2
- var e = Object.create, t = Object.defineProperty, n = Object.getOwnPropertyDescriptor, r = Object.getOwnPropertyNames, i = Object.getPrototypeOf, a = Object.prototype.hasOwnProperty, o = (e, t) => () => (t || e((t = { exports: {} }).exports, t), t.exports), s = (e, n) => {
2
+ var e = Object.create, t = Object.defineProperty, n = Object.getOwnPropertyDescriptor, r = Object.getOwnPropertyNames, i = Object.getPrototypeOf, a = Object.prototype.hasOwnProperty, o = (e, t) => () => (t || (e((t = { exports: {} }).exports, t), e = null), t.exports), s = (e, n) => {
3
3
  let r = {};
4
4
  for (var i in e) t(r, i, {
5
5
  get: e[i],
@@ -1,5 +1,4 @@
1
1
  import { FC } from 'react';
2
2
  import { BrainyRichRootProps } from './types';
3
-
4
3
  export declare const BrainyRichRoot: FC<BrainyRichRootProps>;
5
4
  //# sourceMappingURL=BrainyRichRoot.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"BrainyRichRoot.js","names":[],"sources":["../../src/brainyrich/BrainyRichRoot.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { useEffect } from 'react';\nimport {\n LayoutDashboard,\n Briefcase,\n TrendingUp,\n Target,\n Landmark,\n BarChart3,\n ShieldAlert,\n Calculator,\n UsersRound,\n FileBarChart,\n Bot,\n Building2,\n} from 'lucide-react';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { BrainyRichProvider } from './providers/BrainyRichProvider';\nimport { FeatureFlagProvider } from '@burdenoff/fe-libs/shared/feature-flags';\nimport type { NavItem, BrainyRichRootProps } from './types';\nimport { tWithFallback } from './utils/i18n';\nimport { BrainyRichRoutes } from './BrainyRichRoutes';\nimport { joinPath } from './utils/navigation';\n\nexport const BrainyRichRoot: FC<BrainyRichRootProps> = (props) => {\n const { registerNavItems, registerFooterItems, basePath = '/' } = props;\n const { t } = useI18n();\n const translate = (key: string, fallback: string) => tWithFallback(t, key, fallback);\n\n useEffect(() => {\n if (!registerNavItems) return;\n const navItem: NavItem = {\n id: 'brainyrich',\n label: translate('brainyrich.nav.root', 'BrainyRich'),\n path: basePath,\n icon: <LayoutDashboard size={20} />,\n order: 80,\n group: 'products',\n children: [\n // Overview\n {\n id: 'brainyrich-dashboard',\n label: translate('brainyrich.nav.dashboard', 'Dashboard'),\n path: basePath,\n icon: <LayoutDashboard size={16} />,\n order: 0,\n },\n\n // Wealth Management\n {\n id: 'brainyrich-portfolios',\n label: translate('brainyrich.nav.portfolios', 'Portfolios'),\n path: joinPath(basePath, 'portfolios'),\n icon: <Briefcase size={16} />,\n order: 10,\n },\n {\n id: 'brainyrich-investments',\n label: translate('brainyrich.nav.investments', 'Investments'),\n path: joinPath(basePath, 'investments'),\n icon: <TrendingUp size={16} />,\n order: 11,\n },\n {\n id: 'brainyrich-goals',\n label: translate('brainyrich.nav.goals', 'Goals'),\n path: joinPath(basePath, 'goals'),\n icon: <Target size={16} />,\n order: 12,\n },\n {\n id: 'brainyrich-accounts',\n label: translate('brainyrich.nav.accounts', 'Accounts'),\n path: joinPath(basePath, 'accounts'),\n icon: <Landmark size={16} />,\n order: 13,\n },\n\n // Advisory & Insights\n {\n id: 'brainyrich-analytics',\n label: translate('brainyrich.nav.analytics', 'Analytics'),\n path: joinPath(basePath, 'analytics'),\n icon: <BarChart3 size={16} />,\n order: 21,\n },\n {\n id: 'brainyrich-risk',\n label: translate('brainyrich.nav.risk', 'Rebalancing'),\n path: joinPath(basePath, 'risk'),\n icon: <ShieldAlert size={16} />,\n order: 22,\n },\n\n // Tax\n {\n id: 'brainyrich-tax',\n label: translate('brainyrich.nav.tax', 'Tax'),\n path: joinPath(basePath, 'tax'),\n icon: <Calculator size={16} />,\n order: 30,\n },\n\n // Households & Reports\n {\n id: 'brainyrich-family',\n label: translate('brainyrich.nav.family', 'Households'),\n path: joinPath(basePath, 'family'),\n icon: <UsersRound size={16} />,\n order: 40,\n },\n {\n id: 'brainyrich-reports',\n label: translate('brainyrich.nav.reports', 'Reports'),\n path: joinPath(basePath, 'reports'),\n icon: <FileBarChart size={16} />,\n order: 42,\n },\n {\n id: 'brainyrich-firm',\n label: translate('brainyrich.nav.firm', 'Firm'),\n path: joinPath(basePath, 'firm'),\n icon: <Building2 size={16} />,\n order: 45,\n },\n\n // Insights\n {\n id: 'brainyrich-ai-assistant',\n label: translate('brainyrich.nav.aiAssistant', 'Insights'),\n path: joinPath(basePath, 'ai-assistant'),\n icon: <Bot size={16} />,\n order: 50,\n },\n ],\n };\n return registerNavItems(navItem);\n }, [registerNavItems, basePath, t]);\n\n useEffect(() => {\n if (!registerFooterItems) return;\n return registerFooterItems([\n {\n id: 'brainyrich-status',\n label: translate('brainyrich.footer.ready', 'BrainyRich Ready'),\n type: 'status',\n section: 'left',\n order: 10,\n },\n ]);\n }, [registerFooterItems, t]);\n\n return (\n <FeatureFlagProvider workspaceId={null} gateway=\"global\" defaultEnabled={true}>\n <BrainyRichProvider {...props}>\n <BrainyRichRoutes />\n </BrainyRichProvider>\n </FeatureFlagProvider>\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"BrainyRichRoot.js","names":[],"sources":["../../src/brainyrich/BrainyRichRoot.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { useEffect } from 'react';\nimport {\n LayoutDashboard,\n Briefcase,\n TrendingUp,\n Target,\n Landmark,\n BarChart3,\n ShieldAlert,\n Calculator,\n UsersRound,\n FileBarChart,\n Bot,\n Building2,\n} from 'lucide-react';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { BrainyRichProvider } from './providers/BrainyRichProvider';\nimport { FeatureFlagProvider } from '@burdenoff/fe-libs/shared/feature-flags';\nimport type { NavItem, BrainyRichRootProps } from './types';\nimport { tWithFallback } from './utils/i18n';\nimport { BrainyRichRoutes } from './BrainyRichRoutes';\nimport { joinPath } from './utils/navigation';\n\nexport const BrainyRichRoot: FC<BrainyRichRootProps> = (props) => {\n const { registerNavItems, registerFooterItems, basePath = '/' } = props;\n const { t } = useI18n();\n const translate = (key: string, fallback: string) => tWithFallback(t, key, fallback);\n\n useEffect(() => {\n if (!registerNavItems) return;\n const navItem: NavItem = {\n id: 'brainyrich',\n label: translate('brainyrich.nav.root', 'BrainyRich'),\n path: basePath,\n icon: <LayoutDashboard size={20} />,\n order: 80,\n group: 'products',\n children: [\n // Overview\n {\n id: 'brainyrich-dashboard',\n label: translate('brainyrich.nav.dashboard', 'Dashboard'),\n path: basePath,\n icon: <LayoutDashboard size={16} />,\n order: 0,\n },\n\n // Wealth Management\n {\n id: 'brainyrich-portfolios',\n label: translate('brainyrich.nav.portfolios', 'Portfolios'),\n path: joinPath(basePath, 'portfolios'),\n icon: <Briefcase size={16} />,\n order: 10,\n },\n {\n id: 'brainyrich-investments',\n label: translate('brainyrich.nav.investments', 'Investments'),\n path: joinPath(basePath, 'investments'),\n icon: <TrendingUp size={16} />,\n order: 11,\n },\n {\n id: 'brainyrich-goals',\n label: translate('brainyrich.nav.goals', 'Goals'),\n path: joinPath(basePath, 'goals'),\n icon: <Target size={16} />,\n order: 12,\n },\n {\n id: 'brainyrich-accounts',\n label: translate('brainyrich.nav.accounts', 'Accounts'),\n path: joinPath(basePath, 'accounts'),\n icon: <Landmark size={16} />,\n order: 13,\n },\n\n // Advisory & Insights\n {\n id: 'brainyrich-analytics',\n label: translate('brainyrich.nav.analytics', 'Analytics'),\n path: joinPath(basePath, 'analytics'),\n icon: <BarChart3 size={16} />,\n order: 21,\n },\n {\n id: 'brainyrich-risk',\n label: translate('brainyrich.nav.risk', 'Rebalancing'),\n path: joinPath(basePath, 'risk'),\n icon: <ShieldAlert size={16} />,\n order: 22,\n },\n\n // Tax\n {\n id: 'brainyrich-tax',\n label: translate('brainyrich.nav.tax', 'Tax'),\n path: joinPath(basePath, 'tax'),\n icon: <Calculator size={16} />,\n order: 30,\n },\n\n // Households & Reports\n {\n id: 'brainyrich-family',\n label: translate('brainyrich.nav.family', 'Households'),\n path: joinPath(basePath, 'family'),\n icon: <UsersRound size={16} />,\n order: 40,\n },\n {\n id: 'brainyrich-reports',\n label: translate('brainyrich.nav.reports', 'Reports'),\n path: joinPath(basePath, 'reports'),\n icon: <FileBarChart size={16} />,\n order: 42,\n },\n {\n id: 'brainyrich-firm',\n label: translate('brainyrich.nav.firm', 'Firm'),\n path: joinPath(basePath, 'firm'),\n icon: <Building2 size={16} />,\n order: 45,\n },\n\n // Insights\n {\n id: 'brainyrich-ai-assistant',\n label: translate('brainyrich.nav.aiAssistant', 'Insights'),\n path: joinPath(basePath, 'ai-assistant'),\n icon: <Bot size={16} />,\n order: 50,\n },\n ],\n };\n return registerNavItems(navItem);\n }, [registerNavItems, basePath, t]);\n\n useEffect(() => {\n if (!registerFooterItems) return;\n return registerFooterItems([\n {\n id: 'brainyrich-status',\n label: translate('brainyrich.footer.ready', 'BrainyRich Ready'),\n type: 'status',\n section: 'left',\n order: 10,\n },\n ]);\n }, [registerFooterItems, t]);\n\n return (\n <FeatureFlagProvider workspaceId={null} gateway=\"global\" defaultEnabled={true}>\n <BrainyRichProvider {...props}>\n <BrainyRichRoutes />\n </BrainyRichProvider>\n </FeatureFlagProvider>\n );\n};\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"BrainyRichRoutes.js","names":[],"sources":["../../src/brainyrich/BrainyRichRoutes.tsx"],"sourcesContent":["import { lazy, Suspense, type ReactNode } from 'react';\nimport { Navigate, Route, Routes } from 'react-router';\nimport { Loader2 } from 'lucide-react';\nimport { FeatureGate } from '@burdenoff/fe-libs/shared/feature-flags';\nimport { DashboardPage as IndexPage } from './pages/DashboardPage';\n\nconst AiAssistantPage = lazy(() =>\n import('./pages/AiAssistantPage').then((m) => ({ default: m.AiAssistantPage }))\n);\nconst AnalyticsPage = lazy(() =>\n import('./pages/AnalyticsPage').then((m) => ({ default: m.AnalyticsPage }))\n);\nconst AccountsPage = lazy(() =>\n import('./pages/AccountsPage').then((m) => ({ default: m.AccountsPage }))\n);\nconst FamilyPage = lazy(() =>\n import('./pages/FamilyPage').then((m) => ({ default: m.FamilyPage }))\n);\nconst FirmPage = lazy(() => import('./pages/FirmPage').then((m) => ({ default: m.FirmPage })));\nconst AdminFirmPage = lazy(() =>\n import('./pages/AdminFirmPage').then((m) => ({ default: m.AdminFirmPage }))\n);\nconst GoalsPage = lazy(() => import('./pages/GoalsPage').then((m) => ({ default: m.GoalsPage })));\nconst InvestmentsPage = lazy(() =>\n import('./pages/InvestmentsPage').then((m) => ({ default: m.InvestmentsPage }))\n);\nconst PortfoliosPage = lazy(() =>\n import('./pages/PortfoliosPage').then((m) => ({ default: m.PortfoliosPage }))\n);\nconst ReportsPage = lazy(() =>\n import('./pages/ReportsPage').then((m) => ({ default: m.ReportsPage }))\n);\nconst RiskPage = lazy(() => import('./pages/RiskPage').then((m) => ({ default: m.RiskPage })));\nconst TaxPage = lazy(() => import('./pages/TaxPage').then((m) => ({ default: m.TaxPage })));\n\nfunction PageLoader() {\n return (\n <div className=\"flex min-h-[320px] items-center justify-center\">\n <Loader2 className=\"h-8 w-8 animate-spin text-primary\" />\n </div>\n );\n}\n\nfunction gatedRoute(flag: string, element: ReactNode) {\n return (\n <FeatureGate flag={flag} fallback={<Navigate to=\"\" replace />} defaultValue>\n {element}\n </FeatureGate>\n );\n}\n\nexport function BrainyRichRoutes() {\n return (\n <Suspense fallback={<PageLoader />}>\n <Routes>\n <Route index element={<IndexPage />} />\n <Route\n path=\"ai-assistant\"\n element={gatedRoute('brainyrich.route.aiAssistant', <AiAssistantPage />)}\n />\n <Route\n path=\"analytics\"\n element={gatedRoute('brainyrich.route.analytics', <AnalyticsPage />)}\n />\n <Route\n path=\"accounts\"\n element={gatedRoute('brainyrich.route.accounts', <AccountsPage />)}\n />\n <Route path=\"family\" element={gatedRoute('brainyrich.route.family', <FamilyPage />)} />\n <Route path=\"firm\" element={gatedRoute('brainyrich.route.firm', <FirmPage />)} />\n <Route\n path=\"admin/firm\"\n element={gatedRoute('brainyrich.route.admin.firm', <AdminFirmPage />)}\n />\n <Route path=\"goals\" element={gatedRoute('brainyrich.route.goals', <GoalsPage />)} />\n <Route\n path=\"investments\"\n element={gatedRoute('brainyrich.route.investments', <InvestmentsPage />)}\n />\n <Route\n path=\"portfolios\"\n element={gatedRoute('brainyrich.route.portfolios', <PortfoliosPage />)}\n />\n <Route path=\"reports\" element={gatedRoute('brainyrich.route.reports', <ReportsPage />)} />\n <Route path=\"risk\" element={gatedRoute('brainyrich.route.risk', <RiskPage />)} />\n <Route path=\"tax\" element={gatedRoute('brainyrich.route.tax', <TaxPage />)} />\n <Route path=\"*\" element={<Navigate to=\"\" replace />} />\n </Routes>\n </Suspense>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"BrainyRichRoutes.js","names":[],"sources":["../../src/brainyrich/BrainyRichRoutes.tsx"],"sourcesContent":["import { lazy, Suspense, type ReactNode } from 'react';\nimport { Navigate, Route, Routes } from 'react-router';\nimport { Loader2 } from 'lucide-react';\nimport { FeatureGate } from '@burdenoff/fe-libs/shared/feature-flags';\nimport { DashboardPage as IndexPage } from './pages/DashboardPage';\n\nconst AiAssistantPage = lazy(() =>\n import('./pages/AiAssistantPage').then((m) => ({ default: m.AiAssistantPage }))\n);\nconst AnalyticsPage = lazy(() =>\n import('./pages/AnalyticsPage').then((m) => ({ default: m.AnalyticsPage }))\n);\nconst AccountsPage = lazy(() =>\n import('./pages/AccountsPage').then((m) => ({ default: m.AccountsPage }))\n);\nconst FamilyPage = lazy(() =>\n import('./pages/FamilyPage').then((m) => ({ default: m.FamilyPage }))\n);\nconst FirmPage = lazy(() => import('./pages/FirmPage').then((m) => ({ default: m.FirmPage })));\nconst AdminFirmPage = lazy(() =>\n import('./pages/AdminFirmPage').then((m) => ({ default: m.AdminFirmPage }))\n);\nconst GoalsPage = lazy(() => import('./pages/GoalsPage').then((m) => ({ default: m.GoalsPage })));\nconst InvestmentsPage = lazy(() =>\n import('./pages/InvestmentsPage').then((m) => ({ default: m.InvestmentsPage }))\n);\nconst PortfoliosPage = lazy(() =>\n import('./pages/PortfoliosPage').then((m) => ({ default: m.PortfoliosPage }))\n);\nconst ReportsPage = lazy(() =>\n import('./pages/ReportsPage').then((m) => ({ default: m.ReportsPage }))\n);\nconst RiskPage = lazy(() => import('./pages/RiskPage').then((m) => ({ default: m.RiskPage })));\nconst TaxPage = lazy(() => import('./pages/TaxPage').then((m) => ({ default: m.TaxPage })));\n\nfunction PageLoader() {\n return (\n <div className=\"flex min-h-[320px] items-center justify-center\">\n <Loader2 className=\"h-8 w-8 animate-spin text-primary\" />\n </div>\n );\n}\n\nfunction gatedRoute(flag: string, element: ReactNode) {\n return (\n <FeatureGate flag={flag} fallback={<Navigate to=\"\" replace />} defaultValue>\n {element}\n </FeatureGate>\n );\n}\n\nexport function BrainyRichRoutes() {\n return (\n <Suspense fallback={<PageLoader />}>\n <Routes>\n <Route index element={<IndexPage />} />\n <Route\n path=\"ai-assistant\"\n element={gatedRoute('brainyrich.route.aiAssistant', <AiAssistantPage />)}\n />\n <Route\n path=\"analytics\"\n element={gatedRoute('brainyrich.route.analytics', <AnalyticsPage />)}\n />\n <Route\n path=\"accounts\"\n element={gatedRoute('brainyrich.route.accounts', <AccountsPage />)}\n />\n <Route path=\"family\" element={gatedRoute('brainyrich.route.family', <FamilyPage />)} />\n <Route path=\"firm\" element={gatedRoute('brainyrich.route.firm', <FirmPage />)} />\n <Route\n path=\"admin/firm\"\n element={gatedRoute('brainyrich.route.admin.firm', <AdminFirmPage />)}\n />\n <Route path=\"goals\" element={gatedRoute('brainyrich.route.goals', <GoalsPage />)} />\n <Route\n path=\"investments\"\n element={gatedRoute('brainyrich.route.investments', <InvestmentsPage />)}\n />\n <Route\n path=\"portfolios\"\n element={gatedRoute('brainyrich.route.portfolios', <PortfoliosPage />)}\n />\n <Route path=\"reports\" element={gatedRoute('brainyrich.route.reports', <ReportsPage />)} />\n <Route path=\"risk\" element={gatedRoute('brainyrich.route.risk', <RiskPage />)} />\n <Route path=\"tax\" element={gatedRoute('brainyrich.route.tax', <TaxPage />)} />\n <Route path=\"*\" element={<Navigate to=\"\" replace />} />\n </Routes>\n </Suspense>\n );\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"ActorName.js","names":[],"sources":["../../../src/brainyrich/components/ActorName.tsx"],"sourcesContent":["/**\n * Render an actor UUID (owner / advisor / proposedBy / acknowledgedBy …) as a\n * human-readable display name.\n * @module brainyrich/components/ActorName\n *\n * Wraps fe-libs `useActorProfiles`, which resolves identities via the GLOBAL\n * gateway (workspace-member → org-member → admin lookup → local fallback) with\n * an internal 5-minute cache. The MFE is handed a *workspace* `apiGatewayUrl`;\n * `useActorProfiles` detects that and only uses it for the workspace-scoped\n * tier, resolving the global gateway from the hostname itself — so forwarding\n * our workspace URL is safe. Network failures degrade to a short-id fallback,\n * never an error.\n */\n\nimport { useMemo } from 'react';\nimport { useActorProfiles, type ActorInput } from '@burdenoff/fe-libs/shared';\nimport { useBrainyRich } from '../providers/BrainyRichProvider';\n\nexport interface ActorNameProps {\n /** Actor id (usually a user UUID). */\n id?: string | null;\n /** Actor type — 'user' (default), 'app', 'service', 'super_admin'. */\n actorType?: string;\n className?: string;\n /** Shown when `id` is absent/empty (default \"—\"). */\n fallback?: string;\n /** Also render the secondary label (email / short id) under the name. */\n withSecondary?: boolean;\n}\n\nexport function ActorName({\n id,\n actorType = 'user',\n className = '',\n fallback = '—',\n withSecondary = false,\n}: ActorNameProps) {\n const { apiGatewayUrl, authToken, workspaceId } = useBrainyRich();\n\n // Stable single-actor input list. Hook is always called (never conditional).\n const actors = useMemo<ActorInput[]>(\n () => (id ? [{ actorId: id, actorType }] : []),\n [id, actorType]\n );\n const profiles = useActorProfiles(actors, { apiGatewayUrl, authToken, workspaceId });\n\n if (!id) return <span className={className}>{fallback}</span>;\n\n const profile = profiles[id];\n const displayName = profile?.displayName ?? id;\n\n if (withSecondary && profile?.secondaryLabel && profile.secondaryLabel !== displayName) {\n return (\n <span className={className}>\n <span className=\"text-text-primary\">{displayName}</span>\n <span className=\"block text-xs text-text-secondary\">{profile.secondaryLabel}</span>\n </span>\n );\n }\n\n return (\n <span className={className} title={profile?.email ?? id}>\n {displayName}\n </span>\n );\n}\n"],"mappings":";;;;;AA8BA,SAAgB,EAAU,EACxB,OACA,eAAY,QACZ,eAAY,IACZ,cAAW,KACX,mBAAgB,MACC;CACjB,IAAM,EAAE,kBAAe,cAAW,mBAAgB,GAAe,EAO3D,IAAW,EAJF,QACN,IAAK,CAAC;EAAE,SAAS;EAAI;EAAW,CAAC,GAAG,EAAE,EAC7C,CAAC,GAAI,EAAU,CAChB,EACyC;EAAE;EAAe;EAAW;EAAa,CAAC;AAEpF,KAAI,CAAC,EAAI,QAAO,kBAAC,QAAD;EAAiB;YAAY;EAAgB,CAAA;CAE7D,IAAM,IAAU,EAAS,IACnB,IAAc,GAAS,eAAe;AAW5C,QATI,KAAiB,GAAS,kBAAkB,EAAQ,mBAAmB,IAEvE,kBAAC,QAAD;EAAiB;YAAjB,CACE,kBAAC,QAAD;GAAM,WAAU;aAAqB;GAAmB,CAAA,EACxD,kBAAC,QAAD;GAAM,WAAU;aAAqC,EAAQ;GAAsB,CAAA,CAC9E;MAKT,kBAAC,QAAD;EAAiB;EAAW,OAAO,GAAS,SAAS;YAClD;EACI,CAAA"}
1
+ {"version":3,"file":"ActorName.js","names":[],"sources":["../../../src/brainyrich/components/ActorName.tsx"],"sourcesContent":["/**\n * Render an actor UUID (owner / advisor / proposedBy / acknowledgedBy …) as a\n * human-readable display name.\n * @module brainyrich/components/ActorName\n *\n * Wraps fe-libs `useActorProfiles`, which resolves identities via the GLOBAL\n * gateway (workspace-member → org-member → admin lookup → local fallback) with\n * an internal 5-minute cache. The MFE is handed a *workspace* `apiGatewayUrl`;\n * `useActorProfiles` detects that and only uses it for the workspace-scoped\n * tier, resolving the global gateway from the hostname itself — so forwarding\n * our workspace URL is safe. Network failures degrade to a short-id fallback,\n * never an error.\n */\n\nimport { useMemo } from 'react';\nimport { useActorProfiles, type ActorInput } from '@burdenoff/fe-libs/shared';\nimport { useBrainyRich } from '../providers/BrainyRichProvider';\n\nexport interface ActorNameProps {\n /** Actor id (usually a user UUID). */\n id?: string | null;\n /** Actor type — 'user' (default), 'app', 'service', 'super_admin'. */\n actorType?: string;\n className?: string;\n /** Shown when `id` is absent/empty (default \"—\"). */\n fallback?: string;\n /** Also render the secondary label (email / short id) under the name. */\n withSecondary?: boolean;\n}\n\nexport function ActorName({\n id,\n actorType = 'user',\n className = '',\n fallback = '—',\n withSecondary = false,\n}: ActorNameProps) {\n const { apiGatewayUrl, authToken, workspaceId } = useBrainyRich();\n\n // Stable single-actor input list. Hook is always called (never conditional).\n const actors = useMemo<ActorInput[]>(\n () => (id ? [{ actorId: id, actorType }] : []),\n [id, actorType]\n );\n const profiles = useActorProfiles(actors, { apiGatewayUrl, authToken, workspaceId });\n\n if (!id) return <span className={className}>{fallback}</span>;\n\n const profile = profiles[id];\n const displayName = profile?.displayName ?? id;\n\n if (withSecondary && profile?.secondaryLabel && profile.secondaryLabel !== displayName) {\n return (\n <span className={className}>\n <span className=\"text-text-primary\">{displayName}</span>\n <span className=\"block text-xs text-text-secondary\">{profile.secondaryLabel}</span>\n </span>\n );\n }\n\n return (\n <span className={className} title={profile?.email ?? id}>\n {displayName}\n </span>\n );\n}\n"],"mappings":";;;;;AA8BA,SAAgB,EAAU,EACxB,OACA,eAAY,QACZ,eAAY,IACZ,cAAW,KACX,mBAAgB,MACC;CACjB,IAAM,EAAE,kBAAe,cAAW,mBAAgB,EAAc,GAO1D,IAAW,EAJF,QACN,IAAK,CAAC;EAAE,SAAS;EAAI;CAAU,CAAC,IAAI,CAAC,GAC5C,CAAC,GAAI,CAAS,CAEkB,GAAQ;EAAE;EAAe;EAAW;CAAY,CAAC;CAEnF,IAAI,CAAC,GAAI,OAAO,kBAAC,QAAD;EAAiB;YAAY;CAAe,CAAA;CAE5D,IAAM,IAAU,EAAS,IACnB,IAAc,GAAS,eAAe;CAW5C,OATI,KAAiB,GAAS,kBAAkB,EAAQ,mBAAmB,IAEvE,kBAAC,QAAD;EAAiB;YAAjB,CACE,kBAAC,QAAD;GAAM,WAAU;aAAqB;EAAkB,CAAA,GACvD,kBAAC,QAAD;GAAM,WAAU;aAAqC,EAAQ;EAAqB,CAAA,CAC9E;MAKR,kBAAC,QAAD;EAAiB;EAAW,OAAO,GAAS,SAAS;YAClD;CACG,CAAA;AAEV"}
@@ -1,5 +1,4 @@
1
1
  import { ReactNode } from 'react';
2
-
3
2
  export interface ConfirmDialogProps {
4
3
  open: boolean;
5
4
  onOpenChange: (open: boolean) => void;
@@ -1 +1 @@
1
- {"version":3,"file":"ConfirmDialog.js","names":[],"sources":["../../../src/brainyrich/components/ConfirmDialog.tsx"],"sourcesContent":["/**\n * Shared confirmation dialog for destructive / irreversible actions.\n * @module brainyrich/components/ConfirmDialog\n *\n * Modeled on the archive-confirm dialog previously inlined in AccountsPage so\n * every destructive action (archive account/portfolio/goal/household, delete\n * holding, unlink funding, dismiss insight, reject rebalance) shares one\n * accessible, pending-aware surface.\n */\n\nimport type { ReactNode } from 'react';\nimport {\n Button,\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogDescription,\n ResponsiveDialogFooter,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n} from '@burdenoff/fe-libs/ui';\n\nexport interface ConfirmDialogProps {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n title: string;\n description?: ReactNode;\n /** Label for the confirm button (default \"Confirm\"). */\n confirmLabel?: string;\n /** Label shown while `pending` (default \"Working…\"). */\n pendingLabel?: string;\n cancelLabel?: string;\n /** Render the confirm button with the destructive variant. */\n destructive?: boolean;\n /** Confirm action in flight — disables buttons + swaps the label. */\n pending?: boolean;\n onConfirm: () => void;\n}\n\nexport function ConfirmDialog({\n open,\n onOpenChange,\n title,\n description,\n confirmLabel = 'Confirm',\n pendingLabel = 'Working…',\n cancelLabel = 'Cancel',\n destructive = false,\n pending = false,\n onConfirm,\n}: ConfirmDialogProps) {\n return (\n <ResponsiveDialog open={open} onOpenChange={(next) => !pending && onOpenChange(next)}>\n <ResponsiveDialogContent>\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>{title}</ResponsiveDialogTitle>\n {description ? (\n <ResponsiveDialogDescription>{description}</ResponsiveDialogDescription>\n ) : null}\n </ResponsiveDialogHeader>\n <ResponsiveDialogFooter>\n <Button variant=\"outline\" onClick={() => onOpenChange(false)} disabled={pending}>\n {cancelLabel}\n </Button>\n <Button\n variant={destructive ? 'destructive' : 'default'}\n onClick={onConfirm}\n disabled={pending}\n aria-busy={pending}\n >\n {pending ? pendingLabel : confirmLabel}\n </Button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n );\n}\n"],"mappings":";;;AAsCA,SAAgB,EAAc,EAC5B,SACA,iBACA,UACA,gBACA,kBAAe,WACf,kBAAe,YACf,iBAAc,UACd,iBAAc,IACd,aAAU,IACV,gBACqB;AACrB,QACE,kBAAC,GAAD;EAAwB;EAAM,eAAe,MAAS,CAAC,KAAW,EAAa,EAAK;YAClF,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD,EAAA,UAAwB,GAA8B,CAAA,EACrD,IACC,kBAAC,GAAD,EAAA,UAA8B,GAA0C,CAAA,GACtE,KACmB,EAAA,CAAA,EACzB,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD;GAAQ,SAAQ;GAAU,eAAe,EAAa,GAAM;GAAE,UAAU;aACrE;GACM,CAAA,EACT,kBAAC,GAAD;GACE,SAAS,IAAc,gBAAgB;GACvC,SAAS;GACT,UAAU;GACV,aAAW;aAEV,IAAU,IAAe;GACnB,CAAA,CACc,EAAA,CAAA,CACD,EAAA,CAAA;EACT,CAAA"}
1
+ {"version":3,"file":"ConfirmDialog.js","names":[],"sources":["../../../src/brainyrich/components/ConfirmDialog.tsx"],"sourcesContent":["/**\n * Shared confirmation dialog for destructive / irreversible actions.\n * @module brainyrich/components/ConfirmDialog\n *\n * Modeled on the archive-confirm dialog previously inlined in AccountsPage so\n * every destructive action (archive account/portfolio/goal/household, delete\n * holding, unlink funding, dismiss insight, reject rebalance) shares one\n * accessible, pending-aware surface.\n */\n\nimport type { ReactNode } from 'react';\nimport {\n Button,\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogDescription,\n ResponsiveDialogFooter,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n} from '@burdenoff/fe-libs/ui';\n\nexport interface ConfirmDialogProps {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n title: string;\n description?: ReactNode;\n /** Label for the confirm button (default \"Confirm\"). */\n confirmLabel?: string;\n /** Label shown while `pending` (default \"Working…\"). */\n pendingLabel?: string;\n cancelLabel?: string;\n /** Render the confirm button with the destructive variant. */\n destructive?: boolean;\n /** Confirm action in flight — disables buttons + swaps the label. */\n pending?: boolean;\n onConfirm: () => void;\n}\n\nexport function ConfirmDialog({\n open,\n onOpenChange,\n title,\n description,\n confirmLabel = 'Confirm',\n pendingLabel = 'Working…',\n cancelLabel = 'Cancel',\n destructive = false,\n pending = false,\n onConfirm,\n}: ConfirmDialogProps) {\n return (\n <ResponsiveDialog open={open} onOpenChange={(next) => !pending && onOpenChange(next)}>\n <ResponsiveDialogContent>\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>{title}</ResponsiveDialogTitle>\n {description ? (\n <ResponsiveDialogDescription>{description}</ResponsiveDialogDescription>\n ) : null}\n </ResponsiveDialogHeader>\n <ResponsiveDialogFooter>\n <Button variant=\"outline\" onClick={() => onOpenChange(false)} disabled={pending}>\n {cancelLabel}\n </Button>\n <Button\n variant={destructive ? 'destructive' : 'default'}\n onClick={onConfirm}\n disabled={pending}\n aria-busy={pending}\n >\n {pending ? pendingLabel : confirmLabel}\n </Button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n );\n}\n"],"mappings":";;;AAsCA,SAAgB,EAAc,EAC5B,SACA,iBACA,UACA,gBACA,kBAAe,WACf,kBAAe,YACf,iBAAc,UACd,iBAAc,IACd,aAAU,IACV,gBACqB;CACrB,OACE,kBAAC,GAAD;EAAwB;EAAM,eAAe,MAAS,CAAC,KAAW,EAAa,CAAI;YACjF,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD,EAAA,UAAwB,EAA6B,CAAA,GACpD,IACC,kBAAC,GAAD,EAAA,UAA8B,EAAyC,CAAA,IACrE,IACkB,EAAA,CAAA,GACxB,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD;GAAQ,SAAQ;GAAU,eAAe,EAAa,EAAK;GAAG,UAAU;aACrE;EACK,CAAA,GACR,kBAAC,GAAD;GACE,SAAS,IAAc,gBAAgB;GACvC,SAAS;GACT,UAAU;GACV,aAAW;aAEV,IAAU,IAAe;EACpB,CAAA,CACc,EAAA,CAAA,CACD,EAAA,CAAA;CACT,CAAA;AAEtB"}
@@ -1 +1 @@
1
- {"version":3,"file":"LoadMore.js","names":[],"sources":["../../../src/brainyrich/components/LoadMore.tsx"],"sourcesContent":["/**\n * \"Load more\" button for cursor-paginated list pages.\n * @module brainyrich/components/LoadMore\n *\n * Pair with the `usePaged*` hooks in `../hooks/useBrainyRichApi` (they expose\n * `hasMore`, `loadMore`, `isFetchingNextPage`). Renders nothing when there is\n * no next page, so it can be dropped unconditionally at the end of a list.\n */\n\nimport { Loader2 } from 'lucide-react';\nimport { Button } from '@burdenoff/fe-libs/ui';\n\nexport interface LoadMoreProps {\n /** Whether another page exists. Component renders null when false. */\n hasMore: boolean;\n /** Fetch the next page. */\n onClick: () => void;\n /** Next-page fetch in flight. */\n loading?: boolean;\n label?: string;\n loadingLabel?: string;\n className?: string;\n}\n\nexport function LoadMore({\n hasMore,\n onClick,\n loading = false,\n label = 'Load more',\n loadingLabel = 'Loading…',\n className = '',\n}: LoadMoreProps) {\n if (!hasMore) return null;\n return (\n <div className={`flex justify-center pt-4 ${className}`}>\n <Button variant=\"outline\" size=\"sm\" onClick={onClick} disabled={loading} aria-busy={loading}>\n {loading ? (\n <>\n <Loader2 className=\"size-4 motion-safe:animate-spin\" aria-hidden=\"true\" />\n {loadingLabel}\n </>\n ) : (\n label\n )}\n </Button>\n </div>\n );\n}\n"],"mappings":";;;;AAwBA,SAAgB,EAAS,EACvB,YACA,YACA,aAAU,IACV,WAAQ,aACR,kBAAe,YACf,eAAY,MACI;AAEhB,QADK,IAEH,kBAAC,OAAD;EAAK,WAAW,4BAA4B;YAC1C,kBAAC,GAAD;GAAQ,SAAQ;GAAU,MAAK;GAAc;GAAS,UAAU;GAAS,aAAW;aACjF,IACC,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,GAAD;IAAS,WAAU;IAAkC,eAAY;IAAS,CAAA,EACzE,EACA,EAAA,CAAA,GAEH;GAEK,CAAA;EACL,CAAA,GAba"}
1
+ {"version":3,"file":"LoadMore.js","names":[],"sources":["../../../src/brainyrich/components/LoadMore.tsx"],"sourcesContent":["/**\n * \"Load more\" button for cursor-paginated list pages.\n * @module brainyrich/components/LoadMore\n *\n * Pair with the `usePaged*` hooks in `../hooks/useBrainyRichApi` (they expose\n * `hasMore`, `loadMore`, `isFetchingNextPage`). Renders nothing when there is\n * no next page, so it can be dropped unconditionally at the end of a list.\n */\n\nimport { Loader2 } from 'lucide-react';\nimport { Button } from '@burdenoff/fe-libs/ui';\n\nexport interface LoadMoreProps {\n /** Whether another page exists. Component renders null when false. */\n hasMore: boolean;\n /** Fetch the next page. */\n onClick: () => void;\n /** Next-page fetch in flight. */\n loading?: boolean;\n label?: string;\n loadingLabel?: string;\n className?: string;\n}\n\nexport function LoadMore({\n hasMore,\n onClick,\n loading = false,\n label = 'Load more',\n loadingLabel = 'Loading…',\n className = '',\n}: LoadMoreProps) {\n if (!hasMore) return null;\n return (\n <div className={`flex justify-center pt-4 ${className}`}>\n <Button variant=\"outline\" size=\"sm\" onClick={onClick} disabled={loading} aria-busy={loading}>\n {loading ? (\n <>\n <Loader2 className=\"size-4 motion-safe:animate-spin\" aria-hidden=\"true\" />\n {loadingLabel}\n </>\n ) : (\n label\n )}\n </Button>\n </div>\n );\n}\n"],"mappings":";;;;AAwBA,SAAgB,EAAS,EACvB,YACA,YACA,aAAU,IACV,WAAQ,aACR,kBAAe,YACf,eAAY,MACI;CAEhB,OADK,IAEH,kBAAC,OAAD;EAAK,WAAW,4BAA4B;YAC1C,kBAAC,GAAD;GAAQ,SAAQ;GAAU,MAAK;GAAc;GAAS,UAAU;GAAS,aAAW;aACjF,IACC,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,GAAD;IAAS,WAAU;IAAkC,eAAY;GAAQ,CAAA,GACxE,CACD,EAAA,CAAA,IAEF;EAEI,CAAA;CACL,CAAA,IAbc;AAevB"}
@@ -1 +1 @@
1
- {"version":3,"file":"notify.js","names":[],"sources":["../../../src/brainyrich/components/notify.ts"],"sourcesContent":["/**\n * Centralized user-feedback surface for the BrainyRich MFE.\n * @module brainyrich/components/notify\n *\n * WHY sonner `toast()` and NOT a scoped `<Toaster/>`:\n * sonner exposes a *module-level singleton* store — every `toast()` call\n * renders through whichever `<Toaster/>` is mounted. The host application\n * shell (`AppShellProvider` in `@burdenoff/fe-libs`) already mounts exactly\n * one `<Toaster/>` (see `AppShellProvider.tsx`), so calling `toast()` from\n * here renders through it. Mounting a SECOND `<Toaster/>` in BrainyRichRoot\n * would produce the documented double-toast regression, so we deliberately\n * do not. `sonner` is pinned to the same `^2.0.7` fe-libs uses so the bundler\n * dedupes to a single instance (a duplicated copy would break the singleton).\n *\n * Every page/component and every mutation hook emits feedback through these\n * helpers instead of hand-rolling markup. Durations follow the platform\n * notification spec: success/info auto-dismiss; errors stay until dismissed\n * (sonner renders a close button + announces via aria-live for WCAG AA).\n */\n\nimport { toast } from 'sonner';\nimport { extractBackendError } from '@burdenoff/fe-libs/shared/utils';\n\nexport const notify = {\n success(message: string, description?: string): void {\n toast.success(message, { description, duration: 4000 });\n },\n info(message: string, description?: string): void {\n toast.info(message, { description, duration: 4000 });\n },\n warning(message: string, description?: string): void {\n toast.warning(message, { description, duration: 8000 });\n },\n error(message: string, description?: string): void {\n toast.error(message, { description, duration: Infinity, closeButton: true });\n },\n};\n\n/**\n * Resolve a human-friendly message from any thrown value. The bra transport\n * throws `new Error(<graphql error message>)`, so this runs that message through\n * fe-libs' `extractBackendError` (which maps known codes to friendly copy and\n * hides technical/prisma strings) and falls back to the raw message.\n */\nexport function errorMessage(\n error: unknown,\n fallback = 'Something went wrong. Please try again.'\n): string {\n const extracted = extractBackendError(error);\n if (extracted?.message) return extracted.message;\n if (error instanceof Error && error.message.trim()) return error.message;\n return fallback;\n}\n"],"mappings":";;;AAuBA,IAAa,IAAS;CACpB,QAAQ,GAAiB,GAA4B;AACnD,IAAM,QAAQ,GAAS;GAAE;GAAa,UAAU;GAAM,CAAC;;CAEzD,KAAK,GAAiB,GAA4B;AAChD,IAAM,KAAK,GAAS;GAAE;GAAa,UAAU;GAAM,CAAC;;CAEtD,QAAQ,GAAiB,GAA4B;AACnD,IAAM,QAAQ,GAAS;GAAE;GAAa,UAAU;GAAM,CAAC;;CAEzD,MAAM,GAAiB,GAA4B;AACjD,IAAM,MAAM,GAAS;GAAE;GAAa,UAAU;GAAU,aAAa;GAAM,CAAC;;CAE/E;AAQD,SAAgB,EACd,GACA,IAAW,2CACH;CACR,IAAM,IAAY,EAAoB,EAAM;AAG5C,QAFI,GAAW,UAAgB,EAAU,UACrC,aAAiB,SAAS,EAAM,QAAQ,MAAM,GAAS,EAAM,UAC1D"}
1
+ {"version":3,"file":"notify.js","names":[],"sources":["../../../src/brainyrich/components/notify.ts"],"sourcesContent":["/**\n * Centralized user-feedback surface for the BrainyRich MFE.\n * @module brainyrich/components/notify\n *\n * WHY sonner `toast()` and NOT a scoped `<Toaster/>`:\n * sonner exposes a *module-level singleton* store — every `toast()` call\n * renders through whichever `<Toaster/>` is mounted. The host application\n * shell (`AppShellProvider` in `@burdenoff/fe-libs`) already mounts exactly\n * one `<Toaster/>` (see `AppShellProvider.tsx`), so calling `toast()` from\n * here renders through it. Mounting a SECOND `<Toaster/>` in BrainyRichRoot\n * would produce the documented double-toast regression, so we deliberately\n * do not. `sonner` is pinned to the same `^2.0.7` fe-libs uses so the bundler\n * dedupes to a single instance (a duplicated copy would break the singleton).\n *\n * Every page/component and every mutation hook emits feedback through these\n * helpers instead of hand-rolling markup. Durations follow the platform\n * notification spec: success/info auto-dismiss; errors stay until dismissed\n * (sonner renders a close button + announces via aria-live for WCAG AA).\n */\n\nimport { toast } from 'sonner';\nimport { extractBackendError } from '@burdenoff/fe-libs/shared/utils';\n\nexport const notify = {\n success(message: string, description?: string): void {\n toast.success(message, { description, duration: 4000 });\n },\n info(message: string, description?: string): void {\n toast.info(message, { description, duration: 4000 });\n },\n warning(message: string, description?: string): void {\n toast.warning(message, { description, duration: 8000 });\n },\n error(message: string, description?: string): void {\n toast.error(message, { description, duration: Infinity, closeButton: true });\n },\n};\n\n/**\n * Resolve a human-friendly message from any thrown value. The bra transport\n * throws `new Error(<graphql error message>)`, so this runs that message through\n * fe-libs' `extractBackendError` (which maps known codes to friendly copy and\n * hides technical/prisma strings) and falls back to the raw message.\n */\nexport function errorMessage(\n error: unknown,\n fallback = 'Something went wrong. Please try again.'\n): string {\n const extracted = extractBackendError(error);\n if (extracted?.message) return extracted.message;\n if (error instanceof Error && error.message.trim()) return error.message;\n return fallback;\n}\n"],"mappings":";;;AAuBA,IAAa,IAAS;CACpB,QAAQ,GAAiB,GAA4B;EACnD,EAAM,QAAQ,GAAS;GAAE;GAAa,UAAU;EAAK,CAAC;CACxD;CACA,KAAK,GAAiB,GAA4B;EAChD,EAAM,KAAK,GAAS;GAAE;GAAa,UAAU;EAAK,CAAC;CACrD;CACA,QAAQ,GAAiB,GAA4B;EACnD,EAAM,QAAQ,GAAS;GAAE;GAAa,UAAU;EAAK,CAAC;CACxD;CACA,MAAM,GAAiB,GAA4B;EACjD,EAAM,MAAM,GAAS;GAAE;GAAa,UAAU;GAAU,aAAa;EAAK,CAAC;CAC7E;AACF;AAQA,SAAgB,EACd,GACA,IAAW,2CACH;CACR,IAAM,IAAY,EAAoB,CAAK;CAG3C,OAFI,GAAW,UAAgB,EAAU,UACrC,aAAiB,SAAS,EAAM,QAAQ,KAAK,IAAU,EAAM,UAC1D;AACT"}
@@ -1 +1 @@
1
- {"version":3,"file":"pickers.js","names":[],"sources":["../../../src/brainyrich/components/pickers.tsx"],"sourcesContent":["/**\n * Reusable entity / subject pickers.\n * @module brainyrich/components/pickers\n *\n * - `SubjectPicker` — encodes `SUBJECT_TYPE:id` across accounts, portfolios,\n * goals and households (for the AiAssistant insight subject).\n * - `EntityPicker` — generic account / household Select returning a bare id.\n * - `AccountPicker` — convenience wrapper (kind=\"account\").\n * - `HouseholdPicker` — OPTIONAL household Select with a \"None\" option, for the\n * account create/edit form (accounts can be linked to a\n * household). Never a dead-end when no households exist.\n *\n * When a *required* source list is empty every picker renders a CTA that routes\n * the user to create the missing entity instead of showing an empty dropdown.\n */\n\nimport { Loader2 } from 'lucide-react';\nimport {\n Button,\n Label,\n Select,\n SelectContent,\n SelectGroup,\n SelectItem,\n SelectLabel,\n SelectTrigger,\n SelectValue,\n} from '@burdenoff/fe-libs/ui';\nimport { useAccounts, useGoals, useHouseholds, usePortfolios } from '../hooks/useBrainyRichApi';\nimport { useBrainyRich } from '../providers/BrainyRichProvider';\nimport { joinPath } from '../utils/navigation';\n\n// ============================================================================\n// Subject encoding (matches the SDL `subjectType` convention — uppercase)\n// ============================================================================\n\nexport const SUBJECT_KINDS = ['ACCOUNT', 'PORTFOLIO', 'GOAL', 'HOUSEHOLD'] as const;\nexport type SubjectKind = (typeof SUBJECT_KINDS)[number];\n\nconst SUBJECT_KIND_LABEL: Record<SubjectKind, string> = {\n ACCOUNT: 'Account',\n PORTFOLIO: 'Portfolio',\n GOAL: 'Goal',\n HOUSEHOLD: 'Household',\n};\n\n/** Encode a subject selection as the `SUBJECT_TYPE:id` token a SubjectPicker emits. */\nexport function encodeSubject(kind: SubjectKind, id: string): string {\n return `${kind}:${id}`;\n}\n\n/** Decode a `SUBJECT_TYPE:id` token back into `{ subjectType, subjectId }`. */\nexport function parseSubject(\n value: string | null | undefined\n): { subjectType: SubjectKind; subjectId: string } | null {\n if (!value) return null;\n const idx = value.indexOf(':');\n if (idx < 0) return null;\n const kind = value.slice(0, idx) as SubjectKind;\n const id = value.slice(idx + 1);\n if (!SUBJECT_KINDS.includes(kind) || !id) return null;\n return { subjectType: kind, subjectId: id };\n}\n\n// ============================================================================\n// Shared empty-state CTA (routes to the create surface, never a dead-end)\n// ============================================================================\n\nfunction PickerCTA({\n message,\n cta,\n relativePath,\n}: {\n message: string;\n cta: string;\n relativePath: string;\n}) {\n const { basePath = '/', navigate } = useBrainyRich();\n const target = joinPath(basePath, relativePath);\n return (\n <div className=\"rounded-lg border border-dashed border-border-subtle bg-bg-surface px-4 py-3 text-sm\">\n <p className=\"text-text-secondary\">{message}</p>\n {navigate ? (\n <Button variant=\"outline\" size=\"sm\" className=\"mt-2\" onClick={() => navigate(target)}>\n {cta}\n </Button>\n ) : null}\n </div>\n );\n}\n\nfunction PickerLoading({ id, label }: { id?: string; label?: string }) {\n return (\n <div className=\"space-y-2\">\n {label ? <Label htmlFor={id}>{label}</Label> : null}\n <div\n className=\"flex h-9 items-center gap-2 rounded-md border border-border-subtle bg-bg-surface px-3 text-sm text-text-secondary\"\n aria-busy=\"true\"\n >\n <Loader2 className=\"size-4 motion-safe:animate-spin\" aria-hidden=\"true\" />\n Loading…\n </div>\n </div>\n );\n}\n\n// ============================================================================\n// SubjectPicker — accounts + portfolios + goals + households\n// ============================================================================\n\nexport interface SubjectPickerProps {\n /** Current `SUBJECT_TYPE:id` token, or empty. */\n value?: string;\n onChange: (value: string) => void;\n id?: string;\n label?: string;\n placeholder?: string;\n disabled?: boolean;\n}\n\nexport function SubjectPicker({\n value,\n onChange,\n id = 'subject-picker',\n label = 'Subject',\n placeholder = 'Select a subject',\n disabled = false,\n}: SubjectPickerProps) {\n const accounts = useAccounts();\n const portfolios = usePortfolios();\n const goals = useGoals();\n const households = useHouseholds();\n\n const loading =\n accounts.isLoading || portfolios.isLoading || goals.isLoading || households.isLoading;\n const total =\n accounts.data.length + portfolios.data.length + goals.data.length + households.data.length;\n\n if (loading) return <PickerLoading id={id} label={label} />;\n\n if (total === 0) {\n return (\n <div className=\"space-y-2\">\n {label ? <Label htmlFor={id}>{label}</Label> : null}\n <PickerCTA\n message=\"No accounts, portfolios, goals or households yet — create one to attach this insight.\"\n cta=\"Create an account\"\n relativePath=\"accounts\"\n />\n </div>\n );\n }\n\n const groups: Array<{ kind: SubjectKind; items: Array<{ id: string; name: string }> }> = [\n { kind: 'ACCOUNT', items: accounts.data.map((a) => ({ id: a.id, name: a.name })) },\n { kind: 'PORTFOLIO', items: portfolios.data.map((p) => ({ id: p.id, name: p.name })) },\n { kind: 'GOAL', items: goals.data.map((g) => ({ id: g.id, name: g.name })) },\n { kind: 'HOUSEHOLD', items: households.data.map((h) => ({ id: h.id, name: h.name })) },\n ];\n\n return (\n <div className=\"space-y-2\">\n {label ? <Label htmlFor={id}>{label}</Label> : null}\n <Select value={value || undefined} onValueChange={onChange} disabled={disabled}>\n <SelectTrigger id={id} aria-label={label}>\n <SelectValue placeholder={placeholder} />\n </SelectTrigger>\n <SelectContent>\n {groups\n .filter((group) => group.items.length > 0)\n .map((group) => (\n <SelectGroup key={group.kind}>\n <SelectLabel>{SUBJECT_KIND_LABEL[group.kind]}</SelectLabel>\n {group.items.map((item) => (\n <SelectItem\n key={`${group.kind}:${item.id}`}\n value={encodeSubject(group.kind, item.id)}\n >\n {item.name}\n </SelectItem>\n ))}\n </SelectGroup>\n ))}\n </SelectContent>\n </Select>\n </div>\n );\n}\n\n// ============================================================================\n// EntityPicker — generic account / household id picker\n// ============================================================================\n\nexport interface EntityPickerProps {\n kind: 'account' | 'household';\n value?: string | null;\n onChange: (id: string) => void;\n id?: string;\n label?: string;\n placeholder?: string;\n disabled?: boolean;\n}\n\nconst ENTITY_META: Record<\n EntityPickerProps['kind'],\n { label: string; placeholder: string; relativePath: string; cta: string; emptyMessage: string }\n> = {\n account: {\n label: 'Account',\n placeholder: 'Select an account',\n relativePath: 'accounts',\n cta: 'Create an account',\n emptyMessage: 'No accounts yet — create one first.',\n },\n household: {\n label: 'Household',\n placeholder: 'Select a household',\n relativePath: 'family',\n cta: 'Create a household',\n emptyMessage: 'No households yet — create one first.',\n },\n};\n\nexport function EntityPicker({\n kind,\n value,\n onChange,\n id,\n label,\n placeholder,\n disabled = false,\n}: EntityPickerProps) {\n const meta = ENTITY_META[kind];\n const accounts = useAccounts();\n const households = useHouseholds();\n const source = kind === 'account' ? accounts : households;\n const fieldId = id ?? `${kind}-picker`;\n const fieldLabel = label ?? meta.label;\n\n if (source.isLoading) return <PickerLoading id={fieldId} label={fieldLabel} />;\n\n const options = source.data.map((entity) => ({ id: entity.id, name: entity.name }));\n\n if (options.length === 0) {\n return (\n <div className=\"space-y-2\">\n {fieldLabel ? <Label htmlFor={fieldId}>{fieldLabel}</Label> : null}\n <PickerCTA message={meta.emptyMessage} cta={meta.cta} relativePath={meta.relativePath} />\n </div>\n );\n }\n\n return (\n <div className=\"space-y-2\">\n {fieldLabel ? <Label htmlFor={fieldId}>{fieldLabel}</Label> : null}\n <Select value={value || undefined} onValueChange={onChange} disabled={disabled}>\n <SelectTrigger id={fieldId} aria-label={fieldLabel}>\n <SelectValue placeholder={placeholder ?? meta.placeholder} />\n </SelectTrigger>\n <SelectContent>\n {options.map((option) => (\n <SelectItem key={option.id} value={option.id}>\n {option.name}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n );\n}\n\n/** Convenience wrapper — a required account picker. */\nexport function AccountPicker(props: Omit<EntityPickerProps, 'kind'>) {\n return <EntityPicker kind=\"account\" {...props} />;\n}\n\n// ============================================================================\n// HouseholdPicker — OPTIONAL household link (account form, item 6)\n// ============================================================================\n\nconst HOUSEHOLD_NONE = '__none__';\n\nexport interface HouseholdPickerProps {\n /** Current householdId, or null/undefined for \"no household\". */\n value?: string | null;\n onChange: (householdId: string | null) => void;\n id?: string;\n label?: string;\n /** Label for the \"unlinked\" option (default \"No household\"). */\n noneLabel?: string;\n disabled?: boolean;\n}\n\n/**\n * OPTIONAL household selector. Always offers a \"No household\" option, so it is\n * never a dead-end even when zero households exist (it just shows a hint). Emits\n * `null` for the none option and a bare householdId otherwise.\n */\nexport function HouseholdPicker({\n value,\n onChange,\n id = 'household-picker',\n label = 'Household',\n noneLabel = 'No household',\n disabled = false,\n}: HouseholdPickerProps) {\n const households = useHouseholds();\n\n if (households.isLoading) return <PickerLoading id={id} label={label} />;\n\n return (\n <div className=\"space-y-2\">\n {label ? <Label htmlFor={id}>{label}</Label> : null}\n <Select\n value={value ?? HOUSEHOLD_NONE}\n onValueChange={(next) => onChange(next === HOUSEHOLD_NONE ? null : next)}\n disabled={disabled}\n >\n <SelectTrigger id={id} aria-label={label}>\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value={HOUSEHOLD_NONE}>{noneLabel}</SelectItem>\n {households.data.map((household) => (\n <SelectItem key={household.id} value={household.id}>\n {household.name}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n {households.data.length === 0 ? (\n <p className=\"text-xs text-text-secondary\">\n No households yet — you can create one in Households and link this account later.\n </p>\n ) : null}\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"pickers.js","names":[],"sources":["../../../src/brainyrich/components/pickers.tsx"],"sourcesContent":["/**\n * Reusable entity / subject pickers.\n * @module brainyrich/components/pickers\n *\n * - `SubjectPicker` — encodes `SUBJECT_TYPE:id` across accounts, portfolios,\n * goals and households (for the AiAssistant insight subject).\n * - `EntityPicker` — generic account / household Select returning a bare id.\n * - `AccountPicker` — convenience wrapper (kind=\"account\").\n * - `HouseholdPicker` — OPTIONAL household Select with a \"None\" option, for the\n * account create/edit form (accounts can be linked to a\n * household). Never a dead-end when no households exist.\n *\n * When a *required* source list is empty every picker renders a CTA that routes\n * the user to create the missing entity instead of showing an empty dropdown.\n */\n\nimport { Loader2 } from 'lucide-react';\nimport {\n Button,\n Label,\n Select,\n SelectContent,\n SelectGroup,\n SelectItem,\n SelectLabel,\n SelectTrigger,\n SelectValue,\n} from '@burdenoff/fe-libs/ui';\nimport { useAccounts, useGoals, useHouseholds, usePortfolios } from '../hooks/useBrainyRichApi';\nimport { useBrainyRich } from '../providers/BrainyRichProvider';\nimport { joinPath } from '../utils/navigation';\n\n// ============================================================================\n// Subject encoding (matches the SDL `subjectType` convention — uppercase)\n// ============================================================================\n\nexport const SUBJECT_KINDS = ['ACCOUNT', 'PORTFOLIO', 'GOAL', 'HOUSEHOLD'] as const;\nexport type SubjectKind = (typeof SUBJECT_KINDS)[number];\n\nconst SUBJECT_KIND_LABEL: Record<SubjectKind, string> = {\n ACCOUNT: 'Account',\n PORTFOLIO: 'Portfolio',\n GOAL: 'Goal',\n HOUSEHOLD: 'Household',\n};\n\n/** Encode a subject selection as the `SUBJECT_TYPE:id` token a SubjectPicker emits. */\nexport function encodeSubject(kind: SubjectKind, id: string): string {\n return `${kind}:${id}`;\n}\n\n/** Decode a `SUBJECT_TYPE:id` token back into `{ subjectType, subjectId }`. */\nexport function parseSubject(\n value: string | null | undefined\n): { subjectType: SubjectKind; subjectId: string } | null {\n if (!value) return null;\n const idx = value.indexOf(':');\n if (idx < 0) return null;\n const kind = value.slice(0, idx) as SubjectKind;\n const id = value.slice(idx + 1);\n if (!SUBJECT_KINDS.includes(kind) || !id) return null;\n return { subjectType: kind, subjectId: id };\n}\n\n// ============================================================================\n// Shared empty-state CTA (routes to the create surface, never a dead-end)\n// ============================================================================\n\nfunction PickerCTA({\n message,\n cta,\n relativePath,\n}: {\n message: string;\n cta: string;\n relativePath: string;\n}) {\n const { basePath = '/', navigate } = useBrainyRich();\n const target = joinPath(basePath, relativePath);\n return (\n <div className=\"rounded-lg border border-dashed border-border-subtle bg-bg-surface px-4 py-3 text-sm\">\n <p className=\"text-text-secondary\">{message}</p>\n {navigate ? (\n <Button variant=\"outline\" size=\"sm\" className=\"mt-2\" onClick={() => navigate(target)}>\n {cta}\n </Button>\n ) : null}\n </div>\n );\n}\n\nfunction PickerLoading({ id, label }: { id?: string; label?: string }) {\n return (\n <div className=\"space-y-2\">\n {label ? <Label htmlFor={id}>{label}</Label> : null}\n <div\n className=\"flex h-9 items-center gap-2 rounded-md border border-border-subtle bg-bg-surface px-3 text-sm text-text-secondary\"\n aria-busy=\"true\"\n >\n <Loader2 className=\"size-4 motion-safe:animate-spin\" aria-hidden=\"true\" />\n Loading…\n </div>\n </div>\n );\n}\n\n// ============================================================================\n// SubjectPicker — accounts + portfolios + goals + households\n// ============================================================================\n\nexport interface SubjectPickerProps {\n /** Current `SUBJECT_TYPE:id` token, or empty. */\n value?: string;\n onChange: (value: string) => void;\n id?: string;\n label?: string;\n placeholder?: string;\n disabled?: boolean;\n}\n\nexport function SubjectPicker({\n value,\n onChange,\n id = 'subject-picker',\n label = 'Subject',\n placeholder = 'Select a subject',\n disabled = false,\n}: SubjectPickerProps) {\n const accounts = useAccounts();\n const portfolios = usePortfolios();\n const goals = useGoals();\n const households = useHouseholds();\n\n const loading =\n accounts.isLoading || portfolios.isLoading || goals.isLoading || households.isLoading;\n const total =\n accounts.data.length + portfolios.data.length + goals.data.length + households.data.length;\n\n if (loading) return <PickerLoading id={id} label={label} />;\n\n if (total === 0) {\n return (\n <div className=\"space-y-2\">\n {label ? <Label htmlFor={id}>{label}</Label> : null}\n <PickerCTA\n message=\"No accounts, portfolios, goals or households yet — create one to attach this insight.\"\n cta=\"Create an account\"\n relativePath=\"accounts\"\n />\n </div>\n );\n }\n\n const groups: Array<{ kind: SubjectKind; items: Array<{ id: string; name: string }> }> = [\n { kind: 'ACCOUNT', items: accounts.data.map((a) => ({ id: a.id, name: a.name })) },\n { kind: 'PORTFOLIO', items: portfolios.data.map((p) => ({ id: p.id, name: p.name })) },\n { kind: 'GOAL', items: goals.data.map((g) => ({ id: g.id, name: g.name })) },\n { kind: 'HOUSEHOLD', items: households.data.map((h) => ({ id: h.id, name: h.name })) },\n ];\n\n return (\n <div className=\"space-y-2\">\n {label ? <Label htmlFor={id}>{label}</Label> : null}\n <Select value={value || undefined} onValueChange={onChange} disabled={disabled}>\n <SelectTrigger id={id} aria-label={label}>\n <SelectValue placeholder={placeholder} />\n </SelectTrigger>\n <SelectContent>\n {groups\n .filter((group) => group.items.length > 0)\n .map((group) => (\n <SelectGroup key={group.kind}>\n <SelectLabel>{SUBJECT_KIND_LABEL[group.kind]}</SelectLabel>\n {group.items.map((item) => (\n <SelectItem\n key={`${group.kind}:${item.id}`}\n value={encodeSubject(group.kind, item.id)}\n >\n {item.name}\n </SelectItem>\n ))}\n </SelectGroup>\n ))}\n </SelectContent>\n </Select>\n </div>\n );\n}\n\n// ============================================================================\n// EntityPicker — generic account / household id picker\n// ============================================================================\n\nexport interface EntityPickerProps {\n kind: 'account' | 'household';\n value?: string | null;\n onChange: (id: string) => void;\n id?: string;\n label?: string;\n placeholder?: string;\n disabled?: boolean;\n}\n\nconst ENTITY_META: Record<\n EntityPickerProps['kind'],\n { label: string; placeholder: string; relativePath: string; cta: string; emptyMessage: string }\n> = {\n account: {\n label: 'Account',\n placeholder: 'Select an account',\n relativePath: 'accounts',\n cta: 'Create an account',\n emptyMessage: 'No accounts yet — create one first.',\n },\n household: {\n label: 'Household',\n placeholder: 'Select a household',\n relativePath: 'family',\n cta: 'Create a household',\n emptyMessage: 'No households yet — create one first.',\n },\n};\n\nexport function EntityPicker({\n kind,\n value,\n onChange,\n id,\n label,\n placeholder,\n disabled = false,\n}: EntityPickerProps) {\n const meta = ENTITY_META[kind];\n const accounts = useAccounts();\n const households = useHouseholds();\n const source = kind === 'account' ? accounts : households;\n const fieldId = id ?? `${kind}-picker`;\n const fieldLabel = label ?? meta.label;\n\n if (source.isLoading) return <PickerLoading id={fieldId} label={fieldLabel} />;\n\n const options = source.data.map((entity) => ({ id: entity.id, name: entity.name }));\n\n if (options.length === 0) {\n return (\n <div className=\"space-y-2\">\n {fieldLabel ? <Label htmlFor={fieldId}>{fieldLabel}</Label> : null}\n <PickerCTA message={meta.emptyMessage} cta={meta.cta} relativePath={meta.relativePath} />\n </div>\n );\n }\n\n return (\n <div className=\"space-y-2\">\n {fieldLabel ? <Label htmlFor={fieldId}>{fieldLabel}</Label> : null}\n <Select value={value || undefined} onValueChange={onChange} disabled={disabled}>\n <SelectTrigger id={fieldId} aria-label={fieldLabel}>\n <SelectValue placeholder={placeholder ?? meta.placeholder} />\n </SelectTrigger>\n <SelectContent>\n {options.map((option) => (\n <SelectItem key={option.id} value={option.id}>\n {option.name}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n );\n}\n\n/** Convenience wrapper — a required account picker. */\nexport function AccountPicker(props: Omit<EntityPickerProps, 'kind'>) {\n return <EntityPicker kind=\"account\" {...props} />;\n}\n\n// ============================================================================\n// HouseholdPicker — OPTIONAL household link (account form, item 6)\n// ============================================================================\n\nconst HOUSEHOLD_NONE = '__none__';\n\nexport interface HouseholdPickerProps {\n /** Current householdId, or null/undefined for \"no household\". */\n value?: string | null;\n onChange: (householdId: string | null) => void;\n id?: string;\n label?: string;\n /** Label for the \"unlinked\" option (default \"No household\"). */\n noneLabel?: string;\n disabled?: boolean;\n}\n\n/**\n * OPTIONAL household selector. Always offers a \"No household\" option, so it is\n * never a dead-end even when zero households exist (it just shows a hint). Emits\n * `null` for the none option and a bare householdId otherwise.\n */\nexport function HouseholdPicker({\n value,\n onChange,\n id = 'household-picker',\n label = 'Household',\n noneLabel = 'No household',\n disabled = false,\n}: HouseholdPickerProps) {\n const households = useHouseholds();\n\n if (households.isLoading) return <PickerLoading id={id} label={label} />;\n\n return (\n <div className=\"space-y-2\">\n {label ? <Label htmlFor={id}>{label}</Label> : null}\n <Select\n value={value ?? HOUSEHOLD_NONE}\n onValueChange={(next) => onChange(next === HOUSEHOLD_NONE ? null : next)}\n disabled={disabled}\n >\n <SelectTrigger id={id} aria-label={label}>\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value={HOUSEHOLD_NONE}>{noneLabel}</SelectItem>\n {households.data.map((household) => (\n <SelectItem key={household.id} value={household.id}>\n {household.name}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n {households.data.length === 0 ? (\n <p className=\"text-xs text-text-secondary\">\n No households yet — you can create one in Households and link this account later.\n </p>\n ) : null}\n </div>\n );\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"CommentThread.js","names":[],"sources":["../../../../src/brainyrich/components/reuse/CommentThread.tsx"],"sourcesContent":["/**\n * CommentThread — REUSE of `wspace-conversations-svc` EntityComment as a real\n * comment thread.\n * @module brainyrich/components/reuse/CommentThread\n *\n * Replaces the single-field notes / rationale fork on Households / Portfolios /\n * RebalanceProposals with a threaded, multi-author comment log keyed by\n * `(entityType, entityId)`.\n *\n * Why not the fe-libs `contextConversationWidget`? That folder ships a kit of\n * sub-components + Apollo hooks (subscriptions, `sendContextMessage`, a\n * conversationId-resolution flow) but exports NO mountable root widget and no\n * single mount hook — it is not wrappable as `<CommentThread>` without importing\n * its internals. Per the task's fallback, we build a minimal thread directly on\n * the platform EntityComment API, which is purpose-built for exactly this\n * \"comments on an arbitrary entity\" case:\n * - `getEntityComments(input)` → list (newest first)\n * - `createEntityComment(input)` → post\n * - `deleteEntityComment(input)` → remove own comment (soft)\n *\n * All ops route through the workspace supergraph the MFE already uses.\n * Presentational + self-contained: owns its hooks, reads gateway base URL +\n * current user from `useBrainyRich()`, uses fe-libs/ui + semantic tokens.\n */\n\nimport { useState, type KeyboardEvent } from 'react';\nimport { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';\nimport { MessagesSquare, Send, Trash2 } from 'lucide-react';\nimport { Button, Skeleton, Textarea } from '@burdenoff/fe-libs/ui';\n\nimport { useBrainyRich } from '../../providers/BrainyRichProvider';\nimport { formatDate } from '../../pages/_shared';\nimport { wspaceFetch, wspaceMutate } from './transport';\n\ninterface EntityComment {\n id: string;\n authorId: string;\n authorType: string;\n content: { text: string | null; markdown: string | null };\n createdAt: string;\n edited: boolean;\n status: string;\n}\n\nconst COMMENT_FIELDS = `\n id authorId authorType content { text markdown } createdAt edited status\n`;\n\nconst commentsKey = (entityType: string, entityId: string) =>\n ['brainyrich', 'entity-comments', entityType, entityId] as const;\n\nconst useEntityComments = (entityType: string, entityId: string | undefined) => {\n const { apiGatewayUrl } = useBrainyRich();\n return useQuery({\n queryKey: commentsKey(entityType, entityId ?? ''),\n enabled: Boolean(entityId),\n queryFn: async () => {\n const data = await wspaceFetch<{\n getEntityComments: { items: EntityComment[]; total: number };\n }>(\n `query BraGetEntityComments($input: GetCommentsInput!) {\n getEntityComments(input: $input) {\n items { ${COMMENT_FIELDS} }\n total\n }\n }`,\n {\n input: { entityType, entityId, take: 50, sortBy: 'createdAt', sortOrder: 'desc' },\n },\n apiGatewayUrl\n );\n return data.getEntityComments;\n },\n });\n};\n\nconst useCreateComment = (entityType: string, entityId: string) => {\n const { apiGatewayUrl } = useBrainyRich();\n const qc = useQueryClient();\n return useMutation({\n mutationFn: async (text: string) => {\n const trimmed = text.trim();\n if (!trimmed) throw new Error('A comment is required.');\n const data = await wspaceMutate<{ createEntityComment: { id: string } }>(\n `mutation BraCreateEntityComment($input: CreateEntityCommentInput!) {\n createEntityComment(input: $input) { id }\n }`,\n { input: { entityType, entityId, content: { text: trimmed } } },\n apiGatewayUrl\n );\n return data.createEntityComment;\n },\n onSuccess: () => qc.invalidateQueries({ queryKey: commentsKey(entityType, entityId) }),\n });\n};\n\nconst useDeleteComment = (entityType: string, entityId: string) => {\n const { apiGatewayUrl } = useBrainyRich();\n const qc = useQueryClient();\n return useMutation({\n mutationFn: async (id: string) => {\n const data = await wspaceMutate<{ deleteEntityComment: boolean }>(\n `mutation BraDeleteEntityComment($input: DeleteCommentInput!) {\n deleteEntityComment(input: $input)\n }`,\n { input: { id, soft: true } },\n apiGatewayUrl\n );\n return data.deleteEntityComment;\n },\n onSuccess: () => qc.invalidateQueries({ queryKey: commentsKey(entityType, entityId) }),\n });\n};\n\nconst authorInitials = (authorId: string, isSelf: boolean): string => {\n if (isSelf) return 'You';\n return authorId.slice(0, 2).toUpperCase();\n};\n\nexport interface CommentThreadProps {\n /** Polymorphic entity type, e.g. \"brainyrich-household\". */\n entityType: string;\n /** The bra entity's id (an ID scalar). */\n entityId: string;\n /** Optional heading; hide with `hideHeading`. */\n heading?: string;\n hideHeading?: boolean;\n}\n\nexport function CommentThread({\n entityType,\n entityId,\n heading = 'Discussion',\n hideHeading = false,\n}: CommentThreadProps) {\n const { currentUser } = useBrainyRich();\n const [draft, setDraft] = useState('');\n const comments = useEntityComments(entityType, entityId);\n const create = useCreateComment(entityType, entityId);\n const remove = useDeleteComment(entityType, entityId);\n\n const items = (comments.data?.items ?? []).filter((c) => c.status !== 'DELETED');\n const selfName =\n currentUser?.name ||\n [currentUser?.firstName, currentUser?.lastName].filter(Boolean).join(' ') ||\n currentUser?.email ||\n 'You';\n\n const submit = async () => {\n if (!draft.trim() || create.isPending) return;\n await create.mutateAsync(draft);\n setDraft('');\n };\n\n const onKeyDown = (event: KeyboardEvent<HTMLTextAreaElement>) => {\n if ((event.metaKey || event.ctrlKey) && event.key === 'Enter') {\n event.preventDefault();\n void submit();\n }\n };\n\n return (\n <div className=\"space-y-3\">\n {!hideHeading && (\n <h3 className=\"flex items-center gap-2 text-sm font-semibold text-text-primary\">\n <MessagesSquare className=\"size-4\" aria-hidden=\"true\" />\n {heading}\n {comments.data ? (\n <span className=\"text-xs font-normal text-text-secondary\">({items.length})</span>\n ) : null}\n </h3>\n )}\n\n {/* Composer */}\n <div className=\"space-y-2\">\n <label htmlFor={`comment-${entityId}`} className=\"sr-only\">\n Add a comment\n </label>\n <Textarea\n id={`comment-${entityId}`}\n value={draft}\n onChange={(e) => setDraft(e.target.value)}\n onKeyDown={onKeyDown}\n placeholder=\"Add a note or rationale…\"\n rows={3}\n disabled={create.isPending}\n />\n <div className=\"flex items-center justify-between\">\n <span className=\"text-xs text-text-secondary\">⌘/Ctrl + Enter to post</span>\n <Button\n type=\"button\"\n size=\"sm\"\n onClick={submit}\n disabled={!draft.trim() || create.isPending}\n >\n <Send className=\"size-4\" aria-hidden=\"true\" />\n {create.isPending ? 'Posting…' : 'Post'}\n </Button>\n </div>\n {create.isError && (\n <p className=\"text-sm text-status-error-text\">\n Could not post your comment. Please try again.\n </p>\n )}\n </div>\n\n {/* Thread */}\n {comments.isLoading ? (\n <div className=\"space-y-3\" aria-busy=\"true\">\n <Skeleton className=\"h-16 w-full rounded-md\" />\n <Skeleton className=\"h-16 w-full rounded-md\" />\n </div>\n ) : comments.isError ? (\n <p className=\"text-sm text-status-error-text\">\n Could not load the discussion.{' '}\n <button\n type=\"button\"\n className=\"underline underline-offset-2\"\n onClick={() => comments.refetch()}\n >\n Retry\n </button>\n </p>\n ) : items.length === 0 ? (\n <div className=\"rounded-lg border border-dashed border-border-subtle bg-bg-surface px-4 py-8 text-center\">\n <MessagesSquare className=\"mx-auto mb-2 size-6 text-text-secondary\" aria-hidden=\"true\" />\n <p className=\"text-sm text-text-primary\">No comments yet</p>\n <p className=\"mt-0.5 text-xs text-text-secondary\">\n Start the discussion with a note or rationale.\n </p>\n </div>\n ) : (\n <ul className=\"space-y-3\">\n {items.map((c) => {\n const isSelf = Boolean(currentUser?.id) && c.authorId === currentUser?.id;\n const body = c.content.text ?? c.content.markdown ?? '';\n return (\n <li key={c.id} className=\"flex gap-3\">\n <div\n className=\"flex size-8 shrink-0 items-center justify-center rounded-full bg-bg-sunken text-xs font-medium text-text-secondary\"\n aria-hidden=\"true\"\n >\n {authorInitials(c.authorId, isSelf)}\n </div>\n <div className=\"min-w-0 flex-1 rounded-lg border border-border-subtle bg-bg-surface p-3\">\n <div className=\"mb-1 flex items-center gap-2\">\n <span className=\"text-sm font-medium text-text-primary\">\n {isSelf ? selfName : 'Team member'}\n </span>\n <span className=\"text-xs text-text-secondary\">{formatDate(c.createdAt)}</span>\n {c.edited ? (\n <span className=\"text-xs text-text-secondary\">(edited)</span>\n ) : null}\n {isSelf && (\n <button\n type=\"button\"\n aria-label=\"Delete comment\"\n className=\"ml-auto rounded p-1 text-text-secondary hover:bg-bg-sunken hover:text-status-error-text focus-visible:outline focus-visible:outline-2 focus-visible:outline-border-focus disabled:opacity-50\"\n disabled={remove.isPending}\n onClick={() => remove.mutate(c.id)}\n >\n <Trash2 className=\"size-3.5\" aria-hidden=\"true\" />\n </button>\n )}\n </div>\n <p className=\"whitespace-pre-wrap break-words text-sm text-text-primary\">\n {body}\n </p>\n </div>\n </li>\n );\n })}\n </ul>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"CommentThread.js","names":[],"sources":["../../../../src/brainyrich/components/reuse/CommentThread.tsx"],"sourcesContent":["/**\n * CommentThread — REUSE of `wspace-conversations-svc` EntityComment as a real\n * comment thread.\n * @module brainyrich/components/reuse/CommentThread\n *\n * Replaces the single-field notes / rationale fork on Households / Portfolios /\n * RebalanceProposals with a threaded, multi-author comment log keyed by\n * `(entityType, entityId)`.\n *\n * Why not the fe-libs `contextConversationWidget`? That folder ships a kit of\n * sub-components + Apollo hooks (subscriptions, `sendContextMessage`, a\n * conversationId-resolution flow) but exports NO mountable root widget and no\n * single mount hook — it is not wrappable as `<CommentThread>` without importing\n * its internals. Per the task's fallback, we build a minimal thread directly on\n * the platform EntityComment API, which is purpose-built for exactly this\n * \"comments on an arbitrary entity\" case:\n * - `getEntityComments(input)` → list (newest first)\n * - `createEntityComment(input)` → post\n * - `deleteEntityComment(input)` → remove own comment (soft)\n *\n * All ops route through the workspace supergraph the MFE already uses.\n * Presentational + self-contained: owns its hooks, reads gateway base URL +\n * current user from `useBrainyRich()`, uses fe-libs/ui + semantic tokens.\n */\n\nimport { useState, type KeyboardEvent } from 'react';\nimport { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';\nimport { MessagesSquare, Send, Trash2 } from 'lucide-react';\nimport { Button, Skeleton, Textarea } from '@burdenoff/fe-libs/ui';\n\nimport { useBrainyRich } from '../../providers/BrainyRichProvider';\nimport { formatDate } from '../../pages/_shared';\nimport { wspaceFetch, wspaceMutate } from './transport';\n\ninterface EntityComment {\n id: string;\n authorId: string;\n authorType: string;\n content: { text: string | null; markdown: string | null };\n createdAt: string;\n edited: boolean;\n status: string;\n}\n\nconst COMMENT_FIELDS = `\n id authorId authorType content { text markdown } createdAt edited status\n`;\n\nconst commentsKey = (entityType: string, entityId: string) =>\n ['brainyrich', 'entity-comments', entityType, entityId] as const;\n\nconst useEntityComments = (entityType: string, entityId: string | undefined) => {\n const { apiGatewayUrl } = useBrainyRich();\n return useQuery({\n queryKey: commentsKey(entityType, entityId ?? ''),\n enabled: Boolean(entityId),\n queryFn: async () => {\n const data = await wspaceFetch<{\n getEntityComments: { items: EntityComment[]; total: number };\n }>(\n `query BraGetEntityComments($input: GetCommentsInput!) {\n getEntityComments(input: $input) {\n items { ${COMMENT_FIELDS} }\n total\n }\n }`,\n {\n input: { entityType, entityId, take: 50, sortBy: 'createdAt', sortOrder: 'desc' },\n },\n apiGatewayUrl\n );\n return data.getEntityComments;\n },\n });\n};\n\nconst useCreateComment = (entityType: string, entityId: string) => {\n const { apiGatewayUrl } = useBrainyRich();\n const qc = useQueryClient();\n return useMutation({\n mutationFn: async (text: string) => {\n const trimmed = text.trim();\n if (!trimmed) throw new Error('A comment is required.');\n const data = await wspaceMutate<{ createEntityComment: { id: string } }>(\n `mutation BraCreateEntityComment($input: CreateEntityCommentInput!) {\n createEntityComment(input: $input) { id }\n }`,\n { input: { entityType, entityId, content: { text: trimmed } } },\n apiGatewayUrl\n );\n return data.createEntityComment;\n },\n onSuccess: () => qc.invalidateQueries({ queryKey: commentsKey(entityType, entityId) }),\n });\n};\n\nconst useDeleteComment = (entityType: string, entityId: string) => {\n const { apiGatewayUrl } = useBrainyRich();\n const qc = useQueryClient();\n return useMutation({\n mutationFn: async (id: string) => {\n const data = await wspaceMutate<{ deleteEntityComment: boolean }>(\n `mutation BraDeleteEntityComment($input: DeleteCommentInput!) {\n deleteEntityComment(input: $input)\n }`,\n { input: { id, soft: true } },\n apiGatewayUrl\n );\n return data.deleteEntityComment;\n },\n onSuccess: () => qc.invalidateQueries({ queryKey: commentsKey(entityType, entityId) }),\n });\n};\n\nconst authorInitials = (authorId: string, isSelf: boolean): string => {\n if (isSelf) return 'You';\n return authorId.slice(0, 2).toUpperCase();\n};\n\nexport interface CommentThreadProps {\n /** Polymorphic entity type, e.g. \"brainyrich-household\". */\n entityType: string;\n /** The bra entity's id (an ID scalar). */\n entityId: string;\n /** Optional heading; hide with `hideHeading`. */\n heading?: string;\n hideHeading?: boolean;\n}\n\nexport function CommentThread({\n entityType,\n entityId,\n heading = 'Discussion',\n hideHeading = false,\n}: CommentThreadProps) {\n const { currentUser } = useBrainyRich();\n const [draft, setDraft] = useState('');\n const comments = useEntityComments(entityType, entityId);\n const create = useCreateComment(entityType, entityId);\n const remove = useDeleteComment(entityType, entityId);\n\n const items = (comments.data?.items ?? []).filter((c) => c.status !== 'DELETED');\n const selfName =\n currentUser?.name ||\n [currentUser?.firstName, currentUser?.lastName].filter(Boolean).join(' ') ||\n currentUser?.email ||\n 'You';\n\n const submit = async () => {\n if (!draft.trim() || create.isPending) return;\n await create.mutateAsync(draft);\n setDraft('');\n };\n\n const onKeyDown = (event: KeyboardEvent<HTMLTextAreaElement>) => {\n if ((event.metaKey || event.ctrlKey) && event.key === 'Enter') {\n event.preventDefault();\n void submit();\n }\n };\n\n return (\n <div className=\"space-y-3\">\n {!hideHeading && (\n <h3 className=\"flex items-center gap-2 text-sm font-semibold text-text-primary\">\n <MessagesSquare className=\"size-4\" aria-hidden=\"true\" />\n {heading}\n {comments.data ? (\n <span className=\"text-xs font-normal text-text-secondary\">({items.length})</span>\n ) : null}\n </h3>\n )}\n\n {/* Composer */}\n <div className=\"space-y-2\">\n <label htmlFor={`comment-${entityId}`} className=\"sr-only\">\n Add a comment\n </label>\n <Textarea\n id={`comment-${entityId}`}\n value={draft}\n onChange={(e) => setDraft(e.target.value)}\n onKeyDown={onKeyDown}\n placeholder=\"Add a note or rationale…\"\n rows={3}\n disabled={create.isPending}\n />\n <div className=\"flex items-center justify-between\">\n <span className=\"text-xs text-text-secondary\">⌘/Ctrl + Enter to post</span>\n <Button\n type=\"button\"\n size=\"sm\"\n onClick={submit}\n disabled={!draft.trim() || create.isPending}\n >\n <Send className=\"size-4\" aria-hidden=\"true\" />\n {create.isPending ? 'Posting…' : 'Post'}\n </Button>\n </div>\n {create.isError && (\n <p className=\"text-sm text-status-error-text\">\n Could not post your comment. Please try again.\n </p>\n )}\n </div>\n\n {/* Thread */}\n {comments.isLoading ? (\n <div className=\"space-y-3\" aria-busy=\"true\">\n <Skeleton className=\"h-16 w-full rounded-md\" />\n <Skeleton className=\"h-16 w-full rounded-md\" />\n </div>\n ) : comments.isError ? (\n <p className=\"text-sm text-status-error-text\">\n Could not load the discussion.{' '}\n <button\n type=\"button\"\n className=\"underline underline-offset-2\"\n onClick={() => comments.refetch()}\n >\n Retry\n </button>\n </p>\n ) : items.length === 0 ? (\n <div className=\"rounded-lg border border-dashed border-border-subtle bg-bg-surface px-4 py-8 text-center\">\n <MessagesSquare className=\"mx-auto mb-2 size-6 text-text-secondary\" aria-hidden=\"true\" />\n <p className=\"text-sm text-text-primary\">No comments yet</p>\n <p className=\"mt-0.5 text-xs text-text-secondary\">\n Start the discussion with a note or rationale.\n </p>\n </div>\n ) : (\n <ul className=\"space-y-3\">\n {items.map((c) => {\n const isSelf = Boolean(currentUser?.id) && c.authorId === currentUser?.id;\n const body = c.content.text ?? c.content.markdown ?? '';\n return (\n <li key={c.id} className=\"flex gap-3\">\n <div\n className=\"flex size-8 shrink-0 items-center justify-center rounded-full bg-bg-sunken text-xs font-medium text-text-secondary\"\n aria-hidden=\"true\"\n >\n {authorInitials(c.authorId, isSelf)}\n </div>\n <div className=\"min-w-0 flex-1 rounded-lg border border-border-subtle bg-bg-surface p-3\">\n <div className=\"mb-1 flex items-center gap-2\">\n <span className=\"text-sm font-medium text-text-primary\">\n {isSelf ? selfName : 'Team member'}\n </span>\n <span className=\"text-xs text-text-secondary\">{formatDate(c.createdAt)}</span>\n {c.edited ? (\n <span className=\"text-xs text-text-secondary\">(edited)</span>\n ) : null}\n {isSelf && (\n <button\n type=\"button\"\n aria-label=\"Delete comment\"\n className=\"ml-auto rounded p-1 text-text-secondary hover:bg-bg-sunken hover:text-status-error-text focus-visible:outline focus-visible:outline-2 focus-visible:outline-border-focus disabled:opacity-50\"\n disabled={remove.isPending}\n onClick={() => remove.mutate(c.id)}\n >\n <Trash2 className=\"size-3.5\" aria-hidden=\"true\" />\n </button>\n )}\n </div>\n <p className=\"whitespace-pre-wrap break-words text-sm text-text-primary\">\n {body}\n </p>\n </div>\n </li>\n );\n })}\n </ul>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"DocumentsPanel.js","names":[],"sources":["../../../../src/brainyrich/components/reuse/DocumentsPanel.tsx"],"sourcesContent":["/**\n * DocumentsPanel — REUSE of `wspace-files-svc` for statement / KYC / tax-doc\n * attachments on Accounts + Households.\n * @module brainyrich/components/reuse/DocumentsPanel\n *\n * A wealth product needs to attach documents (statements, KYC, tax packs) to\n * accounts and households. `wspace-files-svc` has no native entity-owned link on\n * `File`, so we scope documents to an entity via a dedicated, stably-named\n * FOLDER per `(entityType, entityId)` and list its contents. All ops route\n * through the workspace supergraph the MFE already uses.\n *\n * Flow:\n * - resolve folder → `getFolders(filter)` matched by name (created lazily on\n * first upload via `createFolder`); no side-effects on read.\n * - list → `getFilesInFolder(folderId, pagination)`\n * - upload (3-step) → `initiateUpload` → S3 `PUT` (byte-progress) → `completeUpload`\n * - download → `getFileDownloadUrl(id, expiresIn)` capped at 21600s to\n * avoid the >604800 fleet bug (S3 7-day cap → 500).\n *\n * Presentational + self-contained: owns its hooks, reads the gateway base URL\n * from `useBrainyRich()`, uses fe-libs/ui primitives + semantic tokens.\n */\n\nimport { useCallback, useRef, useState } from 'react';\nimport { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';\nimport { Download, FileText, Upload } from 'lucide-react';\nimport { Button, Card, CardContent, Skeleton } from '@burdenoff/fe-libs/ui';\n\nimport { useBrainyRich } from '../../providers/BrainyRichProvider';\nimport { formatDate } from '../../pages/_shared';\nimport { wspaceFetch, wspaceMutate } from './transport';\n\n// Presigned-URL TTL: cap at 21600s (6h). >604800 (S3 7-day) → INTERNAL_SERVER_ERROR.\nconst DOWNLOAD_URL_TTL_SECONDS = 21600;\n\ninterface FilesFolder {\n id: string;\n name: string;\n}\n\ninterface FilesFile {\n id: string;\n name: string;\n extension: string;\n mimeType: string;\n size: string;\n createdAt: string;\n}\n\nconst FILE_FIELDS = 'id name extension mimeType size createdAt';\n\nconst folderName = (entityType: string, entityId: string) =>\n `BrainyRich · ${entityType} · ${entityId}`;\n\nconst folderKey = (entityType: string, entityId: string) =>\n ['brainyrich', 'entity-folder', entityType, entityId] as const;\n\nconst filesKey = (folderId: string | null | undefined) =>\n ['brainyrich', 'folder-files', folderId ?? 'none'] as const;\n\n// ============================================================================\n// Folder resolution (read-only — no lazy creation here)\n// ============================================================================\n\nconst useEntityFolder = (entityType: string, entityId: string | undefined) => {\n const { apiGatewayUrl } = useBrainyRich();\n const wanted = entityId ? folderName(entityType, entityId) : '';\n return useQuery({\n queryKey: folderKey(entityType, entityId ?? ''),\n enabled: Boolean(entityId),\n queryFn: async () => {\n const data = await wspaceFetch<{ getFolders: { folders: FilesFolder[] } }>(\n `query BraGetFolders($pagination: PaginationInput) {\n getFolders(pagination: $pagination) { folders { id name } }\n }`,\n { pagination: { limit: 200, offset: 0 } },\n apiGatewayUrl\n );\n return data.getFolders.folders.find((f) => f.name === wanted) ?? null;\n },\n });\n};\n\nconst useFolderFiles = (folderId: string | null | undefined) => {\n const { apiGatewayUrl } = useBrainyRich();\n return useQuery({\n queryKey: filesKey(folderId),\n enabled: Boolean(folderId),\n queryFn: async () => {\n const data = await wspaceFetch<{ getFilesInFolder: { files: FilesFile[] } }>(\n `query BraGetFilesInFolder($folderId: ID, $pagination: PaginationInput) {\n getFilesInFolder(folderId: $folderId, pagination: $pagination) {\n files { ${FILE_FIELDS} }\n }\n }`,\n { folderId, pagination: { limit: 100, offset: 0 } },\n apiGatewayUrl\n );\n return data.getFilesInFolder.files;\n },\n });\n};\n\n// ============================================================================\n// Upload (3-step platform flow, with lazy folder creation)\n// ============================================================================\n\nfunction putWithProgress(\n url: string,\n file: File,\n contentType: string,\n onProgress: (percent: number) => void\n): Promise<void> {\n return new Promise<void>((resolve, reject) => {\n const xhr = new XMLHttpRequest();\n xhr.open('PUT', url);\n xhr.setRequestHeader('Content-Type', contentType);\n xhr.upload.onprogress = (event) => {\n if (event.lengthComputable) {\n onProgress(Math.round((event.loaded / event.total) * 100));\n }\n };\n xhr.onload = () =>\n xhr.status >= 200 && xhr.status < 300\n ? resolve()\n : reject(new Error(`File storage rejected the upload (HTTP ${xhr.status}).`));\n xhr.onerror = () => reject(new Error('Network error while uploading the file.'));\n xhr.onabort = () => reject(new Error('The upload was cancelled.'));\n xhr.send(file);\n });\n}\n\nconst useDocumentUpload = (entityType: string, entityId: string) => {\n const { apiGatewayUrl } = useBrainyRich();\n const qc = useQueryClient();\n const [progress, setProgress] = useState(0);\n\n const mutation = useMutation({\n mutationFn: async (file: File) => {\n setProgress(0);\n\n // 0. resolve or create the entity folder\n let folder = await wspaceFetch<{ getFolders: { folders: FilesFolder[] } }>(\n `query BraResolveFolder($pagination: PaginationInput) {\n getFolders(pagination: $pagination) { folders { id name } }\n }`,\n { pagination: { limit: 200, offset: 0 } },\n apiGatewayUrl\n ).then((d) => d.getFolders.folders.find((f) => f.name === folderName(entityType, entityId)));\n\n if (!folder) {\n const created = await wspaceMutate<{ createFolder: FilesFolder }>(\n `mutation BraCreateFolder($input: CreateFolderInput!) {\n createFolder(input: $input) { id name }\n }`,\n {\n input: {\n name: folderName(entityType, entityId),\n description: `Documents attached to ${entityType} ${entityId}.`,\n visibility: 'WORKSPACE',\n },\n },\n apiGatewayUrl\n );\n folder = created.createFolder;\n }\n const folderId = folder.id;\n\n const dotIndex = file.name.lastIndexOf('.');\n const extension = dotIndex > -1 ? file.name.slice(dotIndex + 1) : '';\n const mimeType = file.type || 'application/octet-stream';\n const fileSize = String(file.size);\n\n // 1. initiate\n const initiated = await wspaceMutate<{\n initiateUpload: { uploadUrl: string | null; session: { id: string } };\n }>(\n `mutation BraInitiateUpload($input: InitiateUploadInput!) {\n initiateUpload(input: $input) { uploadUrl session { id } }\n }`,\n {\n input: {\n fileName: file.name,\n extension,\n mimeType,\n fileSize,\n folderId,\n visibility: 'WORKSPACE',\n },\n },\n apiGatewayUrl\n );\n const { uploadUrl } = initiated.initiateUpload;\n const sessionId = initiated.initiateUpload.session.id;\n if (!uploadUrl) throw new Error('The file service did not return an upload URL.');\n\n // 2. PUT the bytes\n await putWithProgress(uploadUrl, file, mimeType, setProgress);\n\n // 3. complete\n await wspaceMutate<{ completeUpload: { fileId: string } }>(\n `mutation BraCompleteUpload($input: CompleteUploadInput!) {\n completeUpload(input: $input) { fileId }\n }`,\n { input: { sessionId, fileSize, folderId } },\n apiGatewayUrl\n );\n setProgress(100);\n return folderId;\n },\n onSuccess: (folderId) => {\n qc.invalidateQueries({ queryKey: folderKey(entityType, entityId) });\n qc.invalidateQueries({ queryKey: filesKey(folderId) });\n },\n });\n\n return { ...mutation, progress };\n};\n\nconst useDownloadFile = () => {\n const { apiGatewayUrl } = useBrainyRich();\n return useMutation({\n mutationFn: async (id: string) => {\n const data = await wspaceFetch<{ getFileDownloadUrl: string }>(\n `query BraGetFileDownloadUrl($id: ID!, $expiresIn: Int) {\n getFileDownloadUrl(id: $id, expiresIn: $expiresIn)\n }`,\n { id, expiresIn: DOWNLOAD_URL_TTL_SECONDS },\n apiGatewayUrl\n );\n return data.getFileDownloadUrl;\n },\n });\n};\n\n// ============================================================================\n// Component\n// ============================================================================\n\nconst formatSize = (bytes: string): string => {\n const n = Number(bytes);\n if (!Number.isFinite(n) || n <= 0) return '—';\n if (n < 1024) return `${n} B`;\n if (n < 1024 * 1024) return `${(n / 1024).toFixed(0)} KB`;\n return `${(n / (1024 * 1024)).toFixed(1)} MB`;\n};\n\nexport interface DocumentsPanelProps {\n /** Polymorphic entity type, e.g. \"brainyrich-account\" | \"brainyrich-household\". */\n entityType: string;\n /** The bra entity's id (an ID scalar). */\n entityId: string;\n /** Optional heading; hide with `hideHeading`. */\n heading?: string;\n hideHeading?: boolean;\n}\n\nexport function DocumentsPanel({\n entityType,\n entityId,\n heading = 'Documents',\n hideHeading = false,\n}: DocumentsPanelProps) {\n const inputRef = useRef<HTMLInputElement>(null);\n const folder = useEntityFolder(entityType, entityId);\n const files = useFolderFiles(folder.data?.id);\n const upload = useDocumentUpload(entityType, entityId);\n const download = useDownloadFile();\n\n const onPick = useCallback(\n (e: React.ChangeEvent<HTMLInputElement>) => {\n const file = e.target.files?.[0];\n if (file) upload.mutate(file);\n e.target.value = '';\n },\n [upload]\n );\n\n const onDownload = async (id: string) => {\n const url = await download.mutateAsync(id);\n if (url) window.open(url, '_blank', 'noopener,noreferrer');\n };\n\n const loading = folder.isLoading || (Boolean(folder.data) && files.isLoading);\n const list = files.data ?? [];\n\n return (\n <div className=\"space-y-3\">\n <div className=\"flex items-center justify-between\">\n {!hideHeading && (\n <h3 className=\"flex items-center gap-2 text-sm font-semibold text-text-primary\">\n <FileText className=\"size-4\" aria-hidden=\"true\" />\n {heading}\n </h3>\n )}\n <div className=\"ml-auto\">\n <input\n ref={inputRef}\n type=\"file\"\n className=\"sr-only\"\n aria-hidden=\"true\"\n tabIndex={-1}\n onChange={onPick}\n />\n <Button\n type=\"button\"\n size=\"sm\"\n variant=\"outline\"\n onClick={() => inputRef.current?.click()}\n disabled={upload.isPending}\n >\n <Upload className=\"size-4\" aria-hidden=\"true\" />\n {upload.isPending ? `Uploading ${upload.progress}%` : 'Upload'}\n </Button>\n </div>\n </div>\n\n {upload.isPending && (\n <div\n className=\"h-1.5 w-full overflow-hidden rounded-full bg-bg-sunken\"\n role=\"progressbar\"\n aria-label=\"Upload progress\"\n aria-valuenow={upload.progress}\n aria-valuemin={0}\n aria-valuemax={100}\n >\n <div\n className=\"h-full rounded-full bg-primary motion-safe:transition-[width] motion-safe:duration-200\"\n style={{ width: `${upload.progress}%` }}\n />\n </div>\n )}\n\n {loading ? (\n <div className=\"space-y-2\" aria-busy=\"true\">\n <Skeleton className=\"h-12 w-full rounded-md\" />\n <Skeleton className=\"h-12 w-full rounded-md\" />\n </div>\n ) : files.isError ? (\n <p className=\"text-sm text-status-error-text\">\n Could not load documents.{' '}\n <button\n type=\"button\"\n className=\"underline underline-offset-2\"\n onClick={() => files.refetch()}\n >\n Retry\n </button>\n </p>\n ) : list.length === 0 ? (\n <div className=\"rounded-lg border border-dashed border-border-subtle bg-bg-surface px-4 py-8 text-center\">\n <FileText className=\"mx-auto mb-2 size-6 text-text-secondary\" aria-hidden=\"true\" />\n <p className=\"text-sm text-text-primary\">No documents yet</p>\n <p className=\"mt-0.5 text-xs text-text-secondary\">\n Upload statements, KYC or tax documents for this record.\n </p>\n </div>\n ) : (\n <ul className=\"space-y-2\">\n {list.map((file) => (\n <li key={file.id}>\n <Card>\n <CardContent className=\"flex items-center gap-3 p-3\">\n <div className=\"flex size-9 shrink-0 items-center justify-center rounded-md bg-bg-sunken text-text-secondary\">\n <FileText className=\"size-4\" aria-hidden=\"true\" />\n </div>\n <div className=\"min-w-0 flex-1\">\n <p className=\"truncate text-sm font-medium text-text-primary\">{file.name}</p>\n <p className=\"text-xs text-text-secondary\">\n {formatSize(file.size)} · {formatDate(file.createdAt)}\n </p>\n </div>\n <Button\n type=\"button\"\n size=\"sm\"\n variant=\"ghost\"\n aria-label={`Download ${file.name}`}\n disabled={download.isPending}\n onClick={() => onDownload(file.id)}\n >\n <Download className=\"size-4\" aria-hidden=\"true\" />\n </Button>\n </CardContent>\n </Card>\n </li>\n ))}\n </ul>\n )}\n\n {(upload.isError || download.isError) && (\n <p className=\"text-sm text-status-error-text\">\n {upload.error instanceof Error\n ? upload.error.message\n : download.error instanceof Error\n ? 'Could not open the document. Please try again.'\n : 'Something went wrong. Please try again.'}\n </p>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"DocumentsPanel.js","names":[],"sources":["../../../../src/brainyrich/components/reuse/DocumentsPanel.tsx"],"sourcesContent":["/**\n * DocumentsPanel — REUSE of `wspace-files-svc` for statement / KYC / tax-doc\n * attachments on Accounts + Households.\n * @module brainyrich/components/reuse/DocumentsPanel\n *\n * A wealth product needs to attach documents (statements, KYC, tax packs) to\n * accounts and households. `wspace-files-svc` has no native entity-owned link on\n * `File`, so we scope documents to an entity via a dedicated, stably-named\n * FOLDER per `(entityType, entityId)` and list its contents. All ops route\n * through the workspace supergraph the MFE already uses.\n *\n * Flow:\n * - resolve folder → `getFolders(filter)` matched by name (created lazily on\n * first upload via `createFolder`); no side-effects on read.\n * - list → `getFilesInFolder(folderId, pagination)`\n * - upload (3-step) → `initiateUpload` → S3 `PUT` (byte-progress) → `completeUpload`\n * - download → `getFileDownloadUrl(id, expiresIn)` capped at 21600s to\n * avoid the >604800 fleet bug (S3 7-day cap → 500).\n *\n * Presentational + self-contained: owns its hooks, reads the gateway base URL\n * from `useBrainyRich()`, uses fe-libs/ui primitives + semantic tokens.\n */\n\nimport { useCallback, useRef, useState } from 'react';\nimport { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';\nimport { Download, FileText, Upload } from 'lucide-react';\nimport { Button, Card, CardContent, Skeleton } from '@burdenoff/fe-libs/ui';\n\nimport { useBrainyRich } from '../../providers/BrainyRichProvider';\nimport { formatDate } from '../../pages/_shared';\nimport { wspaceFetch, wspaceMutate } from './transport';\n\n// Presigned-URL TTL: cap at 21600s (6h). >604800 (S3 7-day) → INTERNAL_SERVER_ERROR.\nconst DOWNLOAD_URL_TTL_SECONDS = 21600;\n\ninterface FilesFolder {\n id: string;\n name: string;\n}\n\ninterface FilesFile {\n id: string;\n name: string;\n extension: string;\n mimeType: string;\n size: string;\n createdAt: string;\n}\n\nconst FILE_FIELDS = 'id name extension mimeType size createdAt';\n\nconst folderName = (entityType: string, entityId: string) =>\n `BrainyRich · ${entityType} · ${entityId}`;\n\nconst folderKey = (entityType: string, entityId: string) =>\n ['brainyrich', 'entity-folder', entityType, entityId] as const;\n\nconst filesKey = (folderId: string | null | undefined) =>\n ['brainyrich', 'folder-files', folderId ?? 'none'] as const;\n\n// ============================================================================\n// Folder resolution (read-only — no lazy creation here)\n// ============================================================================\n\nconst useEntityFolder = (entityType: string, entityId: string | undefined) => {\n const { apiGatewayUrl } = useBrainyRich();\n const wanted = entityId ? folderName(entityType, entityId) : '';\n return useQuery({\n queryKey: folderKey(entityType, entityId ?? ''),\n enabled: Boolean(entityId),\n queryFn: async () => {\n const data = await wspaceFetch<{ getFolders: { folders: FilesFolder[] } }>(\n `query BraGetFolders($pagination: PaginationInput) {\n getFolders(pagination: $pagination) { folders { id name } }\n }`,\n { pagination: { limit: 200, offset: 0 } },\n apiGatewayUrl\n );\n return data.getFolders.folders.find((f) => f.name === wanted) ?? null;\n },\n });\n};\n\nconst useFolderFiles = (folderId: string | null | undefined) => {\n const { apiGatewayUrl } = useBrainyRich();\n return useQuery({\n queryKey: filesKey(folderId),\n enabled: Boolean(folderId),\n queryFn: async () => {\n const data = await wspaceFetch<{ getFilesInFolder: { files: FilesFile[] } }>(\n `query BraGetFilesInFolder($folderId: ID, $pagination: PaginationInput) {\n getFilesInFolder(folderId: $folderId, pagination: $pagination) {\n files { ${FILE_FIELDS} }\n }\n }`,\n { folderId, pagination: { limit: 100, offset: 0 } },\n apiGatewayUrl\n );\n return data.getFilesInFolder.files;\n },\n });\n};\n\n// ============================================================================\n// Upload (3-step platform flow, with lazy folder creation)\n// ============================================================================\n\nfunction putWithProgress(\n url: string,\n file: File,\n contentType: string,\n onProgress: (percent: number) => void\n): Promise<void> {\n return new Promise<void>((resolve, reject) => {\n const xhr = new XMLHttpRequest();\n xhr.open('PUT', url);\n xhr.setRequestHeader('Content-Type', contentType);\n xhr.upload.onprogress = (event) => {\n if (event.lengthComputable) {\n onProgress(Math.round((event.loaded / event.total) * 100));\n }\n };\n xhr.onload = () =>\n xhr.status >= 200 && xhr.status < 300\n ? resolve()\n : reject(new Error(`File storage rejected the upload (HTTP ${xhr.status}).`));\n xhr.onerror = () => reject(new Error('Network error while uploading the file.'));\n xhr.onabort = () => reject(new Error('The upload was cancelled.'));\n xhr.send(file);\n });\n}\n\nconst useDocumentUpload = (entityType: string, entityId: string) => {\n const { apiGatewayUrl } = useBrainyRich();\n const qc = useQueryClient();\n const [progress, setProgress] = useState(0);\n\n const mutation = useMutation({\n mutationFn: async (file: File) => {\n setProgress(0);\n\n // 0. resolve or create the entity folder\n let folder = await wspaceFetch<{ getFolders: { folders: FilesFolder[] } }>(\n `query BraResolveFolder($pagination: PaginationInput) {\n getFolders(pagination: $pagination) { folders { id name } }\n }`,\n { pagination: { limit: 200, offset: 0 } },\n apiGatewayUrl\n ).then((d) => d.getFolders.folders.find((f) => f.name === folderName(entityType, entityId)));\n\n if (!folder) {\n const created = await wspaceMutate<{ createFolder: FilesFolder }>(\n `mutation BraCreateFolder($input: CreateFolderInput!) {\n createFolder(input: $input) { id name }\n }`,\n {\n input: {\n name: folderName(entityType, entityId),\n description: `Documents attached to ${entityType} ${entityId}.`,\n visibility: 'WORKSPACE',\n },\n },\n apiGatewayUrl\n );\n folder = created.createFolder;\n }\n const folderId = folder.id;\n\n const dotIndex = file.name.lastIndexOf('.');\n const extension = dotIndex > -1 ? file.name.slice(dotIndex + 1) : '';\n const mimeType = file.type || 'application/octet-stream';\n const fileSize = String(file.size);\n\n // 1. initiate\n const initiated = await wspaceMutate<{\n initiateUpload: { uploadUrl: string | null; session: { id: string } };\n }>(\n `mutation BraInitiateUpload($input: InitiateUploadInput!) {\n initiateUpload(input: $input) { uploadUrl session { id } }\n }`,\n {\n input: {\n fileName: file.name,\n extension,\n mimeType,\n fileSize,\n folderId,\n visibility: 'WORKSPACE',\n },\n },\n apiGatewayUrl\n );\n const { uploadUrl } = initiated.initiateUpload;\n const sessionId = initiated.initiateUpload.session.id;\n if (!uploadUrl) throw new Error('The file service did not return an upload URL.');\n\n // 2. PUT the bytes\n await putWithProgress(uploadUrl, file, mimeType, setProgress);\n\n // 3. complete\n await wspaceMutate<{ completeUpload: { fileId: string } }>(\n `mutation BraCompleteUpload($input: CompleteUploadInput!) {\n completeUpload(input: $input) { fileId }\n }`,\n { input: { sessionId, fileSize, folderId } },\n apiGatewayUrl\n );\n setProgress(100);\n return folderId;\n },\n onSuccess: (folderId) => {\n qc.invalidateQueries({ queryKey: folderKey(entityType, entityId) });\n qc.invalidateQueries({ queryKey: filesKey(folderId) });\n },\n });\n\n return { ...mutation, progress };\n};\n\nconst useDownloadFile = () => {\n const { apiGatewayUrl } = useBrainyRich();\n return useMutation({\n mutationFn: async (id: string) => {\n const data = await wspaceFetch<{ getFileDownloadUrl: string }>(\n `query BraGetFileDownloadUrl($id: ID!, $expiresIn: Int) {\n getFileDownloadUrl(id: $id, expiresIn: $expiresIn)\n }`,\n { id, expiresIn: DOWNLOAD_URL_TTL_SECONDS },\n apiGatewayUrl\n );\n return data.getFileDownloadUrl;\n },\n });\n};\n\n// ============================================================================\n// Component\n// ============================================================================\n\nconst formatSize = (bytes: string): string => {\n const n = Number(bytes);\n if (!Number.isFinite(n) || n <= 0) return '—';\n if (n < 1024) return `${n} B`;\n if (n < 1024 * 1024) return `${(n / 1024).toFixed(0)} KB`;\n return `${(n / (1024 * 1024)).toFixed(1)} MB`;\n};\n\nexport interface DocumentsPanelProps {\n /** Polymorphic entity type, e.g. \"brainyrich-account\" | \"brainyrich-household\". */\n entityType: string;\n /** The bra entity's id (an ID scalar). */\n entityId: string;\n /** Optional heading; hide with `hideHeading`. */\n heading?: string;\n hideHeading?: boolean;\n}\n\nexport function DocumentsPanel({\n entityType,\n entityId,\n heading = 'Documents',\n hideHeading = false,\n}: DocumentsPanelProps) {\n const inputRef = useRef<HTMLInputElement>(null);\n const folder = useEntityFolder(entityType, entityId);\n const files = useFolderFiles(folder.data?.id);\n const upload = useDocumentUpload(entityType, entityId);\n const download = useDownloadFile();\n\n const onPick = useCallback(\n (e: React.ChangeEvent<HTMLInputElement>) => {\n const file = e.target.files?.[0];\n if (file) upload.mutate(file);\n e.target.value = '';\n },\n [upload]\n );\n\n const onDownload = async (id: string) => {\n const url = await download.mutateAsync(id);\n if (url) window.open(url, '_blank', 'noopener,noreferrer');\n };\n\n const loading = folder.isLoading || (Boolean(folder.data) && files.isLoading);\n const list = files.data ?? [];\n\n return (\n <div className=\"space-y-3\">\n <div className=\"flex items-center justify-between\">\n {!hideHeading && (\n <h3 className=\"flex items-center gap-2 text-sm font-semibold text-text-primary\">\n <FileText className=\"size-4\" aria-hidden=\"true\" />\n {heading}\n </h3>\n )}\n <div className=\"ml-auto\">\n <input\n ref={inputRef}\n type=\"file\"\n className=\"sr-only\"\n aria-hidden=\"true\"\n tabIndex={-1}\n onChange={onPick}\n />\n <Button\n type=\"button\"\n size=\"sm\"\n variant=\"outline\"\n onClick={() => inputRef.current?.click()}\n disabled={upload.isPending}\n >\n <Upload className=\"size-4\" aria-hidden=\"true\" />\n {upload.isPending ? `Uploading ${upload.progress}%` : 'Upload'}\n </Button>\n </div>\n </div>\n\n {upload.isPending && (\n <div\n className=\"h-1.5 w-full overflow-hidden rounded-full bg-bg-sunken\"\n role=\"progressbar\"\n aria-label=\"Upload progress\"\n aria-valuenow={upload.progress}\n aria-valuemin={0}\n aria-valuemax={100}\n >\n <div\n className=\"h-full rounded-full bg-primary motion-safe:transition-[width] motion-safe:duration-200\"\n style={{ width: `${upload.progress}%` }}\n />\n </div>\n )}\n\n {loading ? (\n <div className=\"space-y-2\" aria-busy=\"true\">\n <Skeleton className=\"h-12 w-full rounded-md\" />\n <Skeleton className=\"h-12 w-full rounded-md\" />\n </div>\n ) : files.isError ? (\n <p className=\"text-sm text-status-error-text\">\n Could not load documents.{' '}\n <button\n type=\"button\"\n className=\"underline underline-offset-2\"\n onClick={() => files.refetch()}\n >\n Retry\n </button>\n </p>\n ) : list.length === 0 ? (\n <div className=\"rounded-lg border border-dashed border-border-subtle bg-bg-surface px-4 py-8 text-center\">\n <FileText className=\"mx-auto mb-2 size-6 text-text-secondary\" aria-hidden=\"true\" />\n <p className=\"text-sm text-text-primary\">No documents yet</p>\n <p className=\"mt-0.5 text-xs text-text-secondary\">\n Upload statements, KYC or tax documents for this record.\n </p>\n </div>\n ) : (\n <ul className=\"space-y-2\">\n {list.map((file) => (\n <li key={file.id}>\n <Card>\n <CardContent className=\"flex items-center gap-3 p-3\">\n <div className=\"flex size-9 shrink-0 items-center justify-center rounded-md bg-bg-sunken text-text-secondary\">\n <FileText className=\"size-4\" aria-hidden=\"true\" />\n </div>\n <div className=\"min-w-0 flex-1\">\n <p className=\"truncate text-sm font-medium text-text-primary\">{file.name}</p>\n <p className=\"text-xs text-text-secondary\">\n {formatSize(file.size)} · {formatDate(file.createdAt)}\n </p>\n </div>\n <Button\n type=\"button\"\n size=\"sm\"\n variant=\"ghost\"\n aria-label={`Download ${file.name}`}\n disabled={download.isPending}\n onClick={() => onDownload(file.id)}\n >\n <Download className=\"size-4\" aria-hidden=\"true\" />\n </Button>\n </CardContent>\n </Card>\n </li>\n ))}\n </ul>\n )}\n\n {(upload.isError || download.isError) && (\n <p className=\"text-sm text-status-error-text\">\n {upload.error instanceof Error\n ? upload.error.message\n : download.error instanceof Error\n ? 'Could not open the document. Please try again.'\n : 'Something went wrong. Please try again.'}\n </p>\n )}\n </div>\n );\n}\n"],"mappings":";;;;;;;;;AAiCA,IAAM,IAA2B,OAgB3B,IAAc,6CAEd,KAAc,GAAoB,MACtC,gBAAgB,EAAW,KAAK,KAE5B,KAAa,GAAoB,MACrC;CAAC;CAAc;CAAiB;CAAY;AAAQ,GAEhD,KAAY,MAChB;CAAC;CAAc;CAAgB,KAAY;AAAM,GAM7C,KAAmB,GAAoB,MAAiC;CAC5E,IAAM,EAAE,qBAAkB,EAAc,GAClC,IAAS,IAAW,EAAW,GAAY,CAAQ,IAAI;CAC7D,OAAO,EAAS;EACd,UAAU,EAAU,GAAY,KAAY,EAAE;EAC9C,SAAS,EAAQ;EACjB,SAAS,aAQA,MAPY,EACjB,yIAGA,EAAE,YAAY;GAAE,OAAO;GAAK,QAAQ;EAAE,EAAE,GACxC,CACF,GACY,WAAW,QAAQ,MAAM,MAAM,EAAE,SAAS,CAAM,KAAK;CAErE,CAAC;AACH,GAEM,KAAkB,MAAwC;CAC9D,IAAM,EAAE,qBAAkB,EAAc;CACxC,OAAO,EAAS;EACd,UAAU,EAAS,CAAQ;EAC3B,SAAS,EAAQ;EACjB,SAAS,aAUA,MATY,EACjB;;sBAEc,EAAY;;YAG1B;GAAE;GAAU,YAAY;IAAE,OAAO;IAAK,QAAQ;GAAE;EAAE,GAClD,CACF,GACY,iBAAiB;CAEjC,CAAC;AACH;AAMA,SAAS,EACP,GACA,GACA,GACA,GACe;CACf,OAAO,IAAI,SAAe,GAAS,MAAW;EAC5C,IAAM,IAAM,IAAI,eAAe;EAc/B,AAbA,EAAI,KAAK,OAAO,CAAG,GACnB,EAAI,iBAAiB,gBAAgB,CAAW,GAChD,EAAI,OAAO,cAAc,MAAU;GACjC,AAAI,EAAM,oBACR,EAAW,KAAK,MAAO,EAAM,SAAS,EAAM,QAAS,GAAG,CAAC;EAE7D,GACA,EAAI,eACF,EAAI,UAAU,OAAO,EAAI,SAAS,MAC9B,EAAQ,IACR,EAAO,gBAAI,MAAM,0CAA0C,EAAI,OAAO,GAAG,CAAC,GAChF,EAAI,gBAAgB,EAAO,gBAAI,MAAM,yCAAyC,CAAC,GAC/E,EAAI,gBAAgB,EAAO,gBAAI,MAAM,2BAA2B,CAAC,GACjE,EAAI,KAAK,CAAI;CACf,CAAC;AACH;AAEA,IAAM,KAAqB,GAAoB,MAAqB;CAClE,IAAM,EAAE,qBAAkB,EAAc,GAClC,IAAK,EAAe,GACpB,CAAC,GAAU,KAAe,EAAS,CAAC;CAiF1C,OAAO;EAAE,GA/EQ,EAAY;GAC3B,YAAY,OAAO,MAAe;IAChC,EAAY,CAAC;IAGb,IAAI,IAAS,MAAM,EACjB,4IAGA,EAAE,YAAY;KAAE,OAAO;KAAK,QAAQ;IAAE,EAAE,GACxC,CACF,EAAE,MAAM,MAAM,EAAE,WAAW,QAAQ,MAAM,MAAM,EAAE,SAAS,EAAW,GAAY,CAAQ,CAAC,CAAC;IAE3F,AAcE,OAAS,MAba,EACpB,4HAGA,EACE,OAAO;KACL,MAAM,EAAW,GAAY,CAAQ;KACrC,aAAa,yBAAyB,EAAW,GAAG,EAAS;KAC7D,YAAY;IACd,EACF,GACA,CACF,GACiB;IAEnB,IAAM,IAAW,EAAO,IAElB,IAAW,EAAK,KAAK,YAAY,GAAG,GACpC,IAAY,IAAW,KAAK,EAAK,KAAK,MAAM,IAAW,CAAC,IAAI,IAC5D,IAAW,EAAK,QAAQ,4BACxB,IAAW,OAAO,EAAK,IAAI,GAG3B,IAAY,MAAM,EAGtB,+IAGA,EACE,OAAO;KACL,UAAU,EAAK;KACf;KACA;KACA;KACA;KACA,YAAY;IACd,EACF,GACA,CACF,GACM,EAAE,iBAAc,EAAU,gBAC1B,IAAY,EAAU,eAAe,QAAQ;IACnD,IAAI,CAAC,GAAW,MAAU,MAAM,gDAAgD;IAchF,OAXA,MAAM,EAAgB,GAAW,GAAM,GAAU,CAAW,GAG5D,MAAM,EACJ,6HAGA,EAAE,OAAO;KAAE;KAAW;KAAU;IAAS,EAAE,GAC3C,CACF,GACA,EAAY,GAAG,GACR;GACT;GACA,YAAY,MAAa;IAEvB,AADA,EAAG,kBAAkB,EAAE,UAAU,EAAU,GAAY,CAAQ,EAAE,CAAC,GAClE,EAAG,kBAAkB,EAAE,UAAU,EAAS,CAAQ,EAAE,CAAC;GACvD;EACF,CAEY;EAAU;CAAS;AACjC,GAEM,UAAwB;CAC5B,IAAM,EAAE,qBAAkB,EAAc;CACxC,OAAO,EAAY,EACjB,YAAY,OAAO,OAQV,MAPY,EACjB,qIAGA;EAAE;EAAI,WAAW;CAAyB,GAC1C,CACF,GACY,mBAEhB,CAAC;AACH,GAMM,KAAc,MAA0B;CAC5C,IAAM,IAAI,OAAO,CAAK;CAItB,OAHI,CAAC,OAAO,SAAS,CAAC,KAAK,KAAK,IAAU,MACtC,IAAI,OAAa,GAAG,EAAE,MACtB,IAAI,OAAO,OAAa,IAAI,IAAI,MAAM,QAAQ,CAAC,EAAE,OAC9C,IAAI,KAAK,OAAO,OAAO,QAAQ,CAAC,EAAE;AAC3C;AAYA,SAAgB,EAAe,EAC7B,eACA,aACA,aAAU,aACV,iBAAc,MACQ;CACtB,IAAM,IAAW,EAAyB,IAAI,GACxC,IAAS,EAAgB,GAAY,CAAQ,GAC7C,IAAQ,EAAe,EAAO,MAAM,EAAE,GACtC,IAAS,EAAkB,GAAY,CAAQ,GAC/C,IAAW,EAAgB,GAE3B,IAAS,GACZ,MAA2C;EAC1C,IAAM,IAAO,EAAE,OAAO,QAAQ;EAE9B,AADI,KAAM,EAAO,OAAO,CAAI,GAC5B,EAAE,OAAO,QAAQ;CACnB,GACA,CAAC,CAAM,CACT,GAEM,IAAa,OAAO,MAAe;EACvC,IAAM,IAAM,MAAM,EAAS,YAAY,CAAE;EACzC,AAAI,KAAK,OAAO,KAAK,GAAK,UAAU,qBAAqB;CAC3D,GAEM,IAAU,EAAO,aAAc,EAAQ,EAAO,QAAS,EAAM,WAC7D,IAAO,EAAM,QAAQ,CAAC;CAE5B,OACE,kBAAC,OAAD;EAAK,WAAU;YAAf;GACE,kBAAC,OAAD;IAAK,WAAU;cAAf,CACG,CAAC,KACA,kBAAC,MAAD;KAAI,WAAU;eAAd,CACE,kBAAC,GAAD;MAAU,WAAU;MAAS,eAAY;KAAQ,CAAA,GAChD,CACC;QAEN,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,SAAD;MACE,KAAK;MACL,MAAK;MACL,WAAU;MACV,eAAY;MACZ,UAAU;MACV,UAAU;KACX,CAAA,GACD,kBAAC,GAAD;MACE,MAAK;MACL,MAAK;MACL,SAAQ;MACR,eAAe,EAAS,SAAS,MAAM;MACvC,UAAU,EAAO;gBALnB,CAOE,kBAAC,GAAD;OAAQ,WAAU;OAAS,eAAY;MAAQ,CAAA,GAC9C,EAAO,YAAY,aAAa,EAAO,SAAS,KAAK,QAChD;OACL;MACF;;GAEJ,EAAO,aACN,kBAAC,OAAD;IACE,WAAU;IACV,MAAK;IACL,cAAW;IACX,iBAAe,EAAO;IACtB,iBAAe;IACf,iBAAe;cAEf,kBAAC,OAAD;KACE,WAAU;KACV,OAAO,EAAE,OAAO,GAAG,EAAO,SAAS,GAAG;IACvC,CAAA;GACE,CAAA;GAGN,IACC,kBAAC,OAAD;IAAK,WAAU;IAAY,aAAU;cAArC,CACE,kBAAC,GAAD,EAAU,WAAU,yBAA0B,CAAA,GAC9C,kBAAC,GAAD,EAAU,WAAU,yBAA0B,CAAA,CAC3C;QACH,EAAM,UACR,kBAAC,KAAD;IAAG,WAAU;cAAb;KAA8C;KAClB;KAC1B,kBAAC,UAAD;MACE,MAAK;MACL,WAAU;MACV,eAAe,EAAM,QAAQ;gBAC9B;KAEO,CAAA;IACP;QACD,EAAK,WAAW,IAClB,kBAAC,OAAD;IAAK,WAAU;cAAf;KACE,kBAAC,GAAD;MAAU,WAAU;MAA0C,eAAY;KAAQ,CAAA;KAClF,kBAAC,KAAD;MAAG,WAAU;gBAA4B;KAAmB,CAAA;KAC5D,kBAAC,KAAD;MAAG,WAAU;gBAAqC;KAE/C,CAAA;IACA;QAEL,kBAAC,MAAD;IAAI,WAAU;cACX,EAAK,KAAK,MACT,kBAAC,MAAD,EAAA,UACE,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD;KAAa,WAAU;eAAvB;MACE,kBAAC,OAAD;OAAK,WAAU;iBACb,kBAAC,GAAD;QAAU,WAAU;QAAS,eAAY;OAAQ,CAAA;MAC9C,CAAA;MACL,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,KAAD;QAAG,WAAU;kBAAkD,EAAK;OAAQ,CAAA,GAC5E,kBAAC,KAAD;QAAG,WAAU;kBAAb;SACG,EAAW,EAAK,IAAI;SAAE;SAAI,EAAW,EAAK,SAAS;QACnD;SACA;;MACL,kBAAC,GAAD;OACE,MAAK;OACL,MAAK;OACL,SAAQ;OACR,cAAY,YAAY,EAAK;OAC7B,UAAU,EAAS;OACnB,eAAe,EAAW,EAAK,EAAE;iBAEjC,kBAAC,GAAD;QAAU,WAAU;QAAS,eAAY;OAAQ,CAAA;MAC3C,CAAA;KACG;OACT,CAAA,EACJ,GAxBK,EAAK,EAwBV,CACL;GACC,CAAA;IAGJ,EAAO,WAAW,EAAS,YAC3B,kBAAC,KAAD;IAAG,WAAU;cACV,EAAO,iBAAiB,QACrB,EAAO,MAAM,UACb,EAAS,iBAAiB,QACxB,mDACA;GACL,CAAA;EAEF;;AAET"}
@@ -1 +1 @@
1
- {"version":3,"file":"TagField.js","names":[],"sources":["../../../../src/brainyrich/components/reuse/TagField.tsx"],"sourcesContent":["/**\n * TagField — REUSE of `wspace-tags-svc` UniversalTag (polymorphic-by-entity).\n * @module brainyrich/components/reuse/TagField\n *\n * Drop-in replacement for the free-text tag string inputs on Households /\n * Portfolios / Goals. Instead of writing a `tags` scalar on the bra entity,\n * this manages REAL `UniversalTag` assignments keyed by `(entityType, entityId)`\n * against the platform tag store — no bra-svc change required. The bra `tags`\n * scalar stays as legacy; this component is the canonical tag surface.\n *\n * Ops (all on the workspace supergraph):\n * - `getEntityTags(entityType, entityId)` → current assignments (chips)\n * - `searchTags(query, limit)` → typeahead over existing tags\n * - `assignTag(input)` → link an existing tag\n * - `createTag(input)` + `assignTag(...)` → create-from-label then link\n * - `removeTagAssignment(id)` → unlink a chip\n *\n * `createTag` needs a `vocabularyId`; we resolve a stable, workspace-scoped\n * \"brainyrich\" vocabulary via `getVocabularyByKey` (creating it on first use).\n *\n * Presentational + self-contained: owns its react-query hooks, reads the gateway\n * base URL from `useBrainyRich()`, uses fe-libs/ui primitives + semantic tokens.\n */\n\nimport { useState, type KeyboardEvent } from 'react';\nimport { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';\nimport { Plus, Tag as TagIcon, X } from 'lucide-react';\nimport { Badge, Button, Input, Label, Skeleton } from '@burdenoff/fe-libs/ui';\n\nimport { useBrainyRich } from '../../providers/BrainyRichProvider';\nimport { wspaceFetch, wspaceMutate } from './transport';\n\n// ============================================================================\n// Types (mirror the UniversalTag SDL fields we select)\n// ============================================================================\n\ninterface UniversalTag {\n id: string;\n label: string;\n color: string | null;\n key: string;\n}\n\ninterface UniversalTagAssignment {\n id: string;\n tag: UniversalTag;\n}\n\nconst TAG_FIELDS = 'id label color key';\nconst BRAINYRICH_VOCABULARY_KEY = 'brainyrich';\n\nconst entityTagsKey = (entityType: string, entityId: string) =>\n ['brainyrich', 'entity-tags', entityType, entityId] as const;\n\nconst slugify = (value: string): string =>\n value\n .toLowerCase()\n .trim()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '')\n .slice(0, 48) || 'tag';\n\n// ============================================================================\n// Hooks (co-located; reuse the shared workspace transport)\n// ============================================================================\n\nconst useEntityTags = (entityType: string, entityId: string | undefined) => {\n const { apiGatewayUrl } = useBrainyRich();\n return useQuery({\n queryKey: entityTagsKey(entityType, entityId ?? ''),\n enabled: Boolean(entityId),\n queryFn: async () => {\n const data = await wspaceFetch<{ getEntityTags: UniversalTagAssignment[] }>(\n `query BraGetEntityTags($entityType: String!, $entityId: ID!) {\n getEntityTags(entityType: $entityType, entityId: $entityId) {\n id\n tag { ${TAG_FIELDS} }\n }\n }`,\n { entityType, entityId },\n apiGatewayUrl\n );\n return data.getEntityTags;\n },\n });\n};\n\nconst useTagSearch = (query: string) => {\n const { apiGatewayUrl } = useBrainyRich();\n const trimmed = query.trim();\n return useQuery({\n queryKey: ['brainyrich', 'tag-search', trimmed],\n enabled: trimmed.length > 0,\n queryFn: async () => {\n const data = await wspaceFetch<{ searchTags: UniversalTag[] }>(\n `query BraSearchTags($query: String!, $limit: Int) {\n searchTags(query: $query, limit: $limit) { ${TAG_FIELDS} }\n }`,\n { query: trimmed, limit: 8 },\n apiGatewayUrl\n );\n return data.searchTags;\n },\n });\n};\n\n/** Resolve (creating on first use) the workspace-scoped \"brainyrich\" vocabulary. */\nconst resolveVocabularyId = async (baseUrl?: string): Promise<string> => {\n const existing = await wspaceFetch<{ getVocabularyByKey: { id: string } | null }>(\n `query BraGetVocabulary($key: String!) {\n getVocabularyByKey(key: $key) { id }\n }`,\n { key: BRAINYRICH_VOCABULARY_KEY },\n baseUrl\n );\n if (existing.getVocabularyByKey?.id) return existing.getVocabularyByKey.id;\n\n const created = await wspaceMutate<{ createVocabulary: { id: string } }>(\n `mutation BraCreateVocabulary($input: CreateVocabularyInput!) {\n createVocabulary(input: $input) { id }\n }`,\n {\n input: {\n key: BRAINYRICH_VOCABULARY_KEY,\n name: 'BrainyRich',\n description: 'Tags for BrainyRich households, portfolios and goals.',\n allowCustomTags: true,\n },\n },\n baseUrl\n );\n return created.createVocabulary.id;\n};\n\nconst useAssignTag = (entityType: string, entityId: string) => {\n const { apiGatewayUrl } = useBrainyRich();\n const qc = useQueryClient();\n return useMutation({\n mutationFn: async (tagId: string) => {\n const data = await wspaceMutate<{ assignTag: { id: string } }>(\n `mutation BraAssignTag($input: AssignTagInput!) {\n assignTag(input: $input) { id }\n }`,\n { input: { entityType, entityId, tagId } },\n apiGatewayUrl\n );\n return data.assignTag;\n },\n onSuccess: () => qc.invalidateQueries({ queryKey: entityTagsKey(entityType, entityId) }),\n });\n};\n\nconst useCreateAndAssignTag = (entityType: string, entityId: string) => {\n const { apiGatewayUrl } = useBrainyRich();\n const qc = useQueryClient();\n return useMutation({\n mutationFn: async (label: string) => {\n const trimmed = label.trim();\n if (!trimmed) throw new Error('A tag label is required.');\n const vocabularyId = await resolveVocabularyId(apiGatewayUrl);\n // Unique key avoids collisions inside the vocabulary; the label is the display text.\n const key = `${slugify(trimmed)}-${Math.random().toString(36).slice(2, 6)}`;\n const created = await wspaceMutate<{ createTag: { id: string } }>(\n `mutation BraCreateTag($input: CreateTagInput!) {\n createTag(input: $input) { id }\n }`,\n { input: { vocabularyId, key, label: trimmed } },\n apiGatewayUrl\n );\n await wspaceMutate<{ assignTag: { id: string } }>(\n `mutation BraAssignCreatedTag($input: AssignTagInput!) {\n assignTag(input: $input) { id }\n }`,\n { input: { entityType, entityId, tagId: created.createTag.id } },\n apiGatewayUrl\n );\n return created.createTag;\n },\n onSuccess: () => qc.invalidateQueries({ queryKey: entityTagsKey(entityType, entityId) }),\n });\n};\n\nconst useRemoveTagAssignment = (entityType: string, entityId: string) => {\n const { apiGatewayUrl } = useBrainyRich();\n const qc = useQueryClient();\n return useMutation({\n mutationFn: async (assignmentId: string) => {\n const data = await wspaceMutate<{ removeTagAssignment: boolean }>(\n `mutation BraRemoveTagAssignment($id: ID!) { removeTagAssignment(id: $id) }`,\n { id: assignmentId },\n apiGatewayUrl\n );\n return data.removeTagAssignment;\n },\n onSuccess: () => qc.invalidateQueries({ queryKey: entityTagsKey(entityType, entityId) }),\n });\n};\n\n// ============================================================================\n// Component\n// ============================================================================\n\nexport interface TagFieldProps {\n /** Polymorphic entity type, e.g. \"brainyrich-household\". */\n entityType: string;\n /** The bra entity's id (an ID scalar). */\n entityId: string;\n /** Optional field label (hidden visually when `hideLabel`). */\n label?: string;\n hideLabel?: boolean;\n}\n\nexport function TagField({\n entityType,\n entityId,\n label = 'Tags',\n hideLabel = false,\n}: TagFieldProps) {\n const [draft, setDraft] = useState('');\n const assignments = useEntityTags(entityType, entityId);\n const search = useTagSearch(draft);\n const assign = useAssignTag(entityType, entityId);\n const createAndAssign = useCreateAndAssignTag(entityType, entityId);\n const remove = useRemoveTagAssignment(entityType, entityId);\n\n const busy = assign.isPending || createAndAssign.isPending;\n const assigned = assignments.data ?? [];\n const assignedTagIds = new Set(assigned.map((a) => a.tag.id));\n const suggestions = (search.data ?? []).filter((t) => !assignedTagIds.has(t.id));\n const trimmed = draft.trim();\n const exactExists = suggestions.some((t) => t.label.toLowerCase() === trimmed.toLowerCase());\n\n const inputId = `tagfield-${entityType}-${entityId}`;\n\n const commitCreate = async () => {\n if (!trimmed || busy) return;\n await createAndAssign.mutateAsync(trimmed);\n setDraft('');\n };\n\n const commitAssign = async (tagId: string) => {\n if (busy) return;\n await assign.mutateAsync(tagId);\n setDraft('');\n };\n\n const onKeyDown = (event: KeyboardEvent<HTMLInputElement>) => {\n if (event.key === 'Enter') {\n event.preventDefault();\n void commitCreate();\n }\n };\n\n return (\n <div className=\"space-y-2\">\n {!hideLabel && (\n <Label htmlFor={inputId} className=\"text-text-primary\">\n {label}\n </Label>\n )}\n\n {/* Current assignments */}\n {assignments.isLoading ? (\n <div className=\"flex flex-wrap gap-2\" aria-busy=\"true\">\n <Skeleton className=\"h-6 w-16 rounded-full\" />\n <Skeleton className=\"h-6 w-20 rounded-full\" />\n <Skeleton className=\"h-6 w-12 rounded-full\" />\n </div>\n ) : assignments.isError ? (\n <p className=\"text-sm text-status-error-text\">\n Could not load tags.{' '}\n <button\n type=\"button\"\n className=\"underline underline-offset-2\"\n onClick={() => assignments.refetch()}\n >\n Retry\n </button>\n </p>\n ) : assigned.length === 0 ? (\n <p className=\"flex items-center gap-1.5 text-sm text-text-secondary\">\n <TagIcon className=\"size-3.5\" aria-hidden=\"true\" />\n No tags yet — add one below.\n </p>\n ) : (\n <div className=\"flex flex-wrap gap-2\">\n {assigned.map((a) => (\n <Badge key={a.id} variant=\"secondary\" className=\"gap-1 pr-1\">\n <span>{a.tag.label}</span>\n <button\n type=\"button\"\n aria-label={`Remove tag ${a.tag.label}`}\n className=\"rounded-full p-0.5 text-text-secondary hover:bg-bg-sunken hover:text-text-primary focus-visible:outline focus-visible:outline-2 focus-visible:outline-border-focus disabled:opacity-50\"\n disabled={remove.isPending}\n onClick={() => remove.mutate(a.id)}\n >\n <X className=\"size-3\" aria-hidden=\"true\" />\n </button>\n </Badge>\n ))}\n </div>\n )}\n\n {/* Add control */}\n <div className=\"relative\">\n <div className=\"flex gap-2\">\n <Input\n id={inputId}\n value={draft}\n onChange={(e) => setDraft(e.target.value)}\n onKeyDown={onKeyDown}\n placeholder=\"Search or create a tag…\"\n aria-label=\"Search or create a tag\"\n autoComplete=\"off\"\n disabled={busy}\n />\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={commitCreate}\n disabled={!trimmed || busy || exactExists}\n >\n <Plus className=\"size-4\" aria-hidden=\"true\" />\n Create\n </Button>\n </div>\n\n {/* Typeahead suggestions */}\n {trimmed.length > 0 && (suggestions.length > 0 || search.isFetching) && (\n <ul\n className=\"absolute z-10 mt-1 w-full overflow-hidden rounded-md border border-border-subtle bg-bg-surface shadow-md\"\n role=\"listbox\"\n aria-label=\"Tag suggestions\"\n >\n {search.isFetching && suggestions.length === 0 ? (\n <li className=\"px-3 py-2 text-sm text-text-secondary\">Searching…</li>\n ) : (\n suggestions.map((t) => (\n <li key={t.id} role=\"option\" aria-selected=\"false\">\n <button\n type=\"button\"\n className=\"flex w-full items-center gap-2 px-3 py-2 text-left text-sm text-text-primary hover:bg-bg-sunken focus-visible:bg-bg-sunken focus-visible:outline-none disabled:opacity-50\"\n disabled={busy}\n onClick={() => commitAssign(t.id)}\n >\n <TagIcon className=\"size-3.5 text-text-secondary\" aria-hidden=\"true\" />\n {t.label}\n </button>\n </li>\n ))\n )}\n </ul>\n )}\n </div>\n\n {(assign.isError || createAndAssign.isError || remove.isError) && (\n <p className=\"text-sm text-status-error-text\">Could not update tags. Please try again.</p>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"TagField.js","names":[],"sources":["../../../../src/brainyrich/components/reuse/TagField.tsx"],"sourcesContent":["/**\n * TagField — REUSE of `wspace-tags-svc` UniversalTag (polymorphic-by-entity).\n * @module brainyrich/components/reuse/TagField\n *\n * Drop-in replacement for the free-text tag string inputs on Households /\n * Portfolios / Goals. Instead of writing a `tags` scalar on the bra entity,\n * this manages REAL `UniversalTag` assignments keyed by `(entityType, entityId)`\n * against the platform tag store — no bra-svc change required. The bra `tags`\n * scalar stays as legacy; this component is the canonical tag surface.\n *\n * Ops (all on the workspace supergraph):\n * - `getEntityTags(entityType, entityId)` → current assignments (chips)\n * - `searchTags(query, limit)` → typeahead over existing tags\n * - `assignTag(input)` → link an existing tag\n * - `createTag(input)` + `assignTag(...)` → create-from-label then link\n * - `removeTagAssignment(id)` → unlink a chip\n *\n * `createTag` needs a `vocabularyId`; we resolve a stable, workspace-scoped\n * \"brainyrich\" vocabulary via `getVocabularyByKey` (creating it on first use).\n *\n * Presentational + self-contained: owns its react-query hooks, reads the gateway\n * base URL from `useBrainyRich()`, uses fe-libs/ui primitives + semantic tokens.\n */\n\nimport { useState, type KeyboardEvent } from 'react';\nimport { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';\nimport { Plus, Tag as TagIcon, X } from 'lucide-react';\nimport { Badge, Button, Input, Label, Skeleton } from '@burdenoff/fe-libs/ui';\n\nimport { useBrainyRich } from '../../providers/BrainyRichProvider';\nimport { wspaceFetch, wspaceMutate } from './transport';\n\n// ============================================================================\n// Types (mirror the UniversalTag SDL fields we select)\n// ============================================================================\n\ninterface UniversalTag {\n id: string;\n label: string;\n color: string | null;\n key: string;\n}\n\ninterface UniversalTagAssignment {\n id: string;\n tag: UniversalTag;\n}\n\nconst TAG_FIELDS = 'id label color key';\nconst BRAINYRICH_VOCABULARY_KEY = 'brainyrich';\n\nconst entityTagsKey = (entityType: string, entityId: string) =>\n ['brainyrich', 'entity-tags', entityType, entityId] as const;\n\nconst slugify = (value: string): string =>\n value\n .toLowerCase()\n .trim()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '')\n .slice(0, 48) || 'tag';\n\n// ============================================================================\n// Hooks (co-located; reuse the shared workspace transport)\n// ============================================================================\n\nconst useEntityTags = (entityType: string, entityId: string | undefined) => {\n const { apiGatewayUrl } = useBrainyRich();\n return useQuery({\n queryKey: entityTagsKey(entityType, entityId ?? ''),\n enabled: Boolean(entityId),\n queryFn: async () => {\n const data = await wspaceFetch<{ getEntityTags: UniversalTagAssignment[] }>(\n `query BraGetEntityTags($entityType: String!, $entityId: ID!) {\n getEntityTags(entityType: $entityType, entityId: $entityId) {\n id\n tag { ${TAG_FIELDS} }\n }\n }`,\n { entityType, entityId },\n apiGatewayUrl\n );\n return data.getEntityTags;\n },\n });\n};\n\nconst useTagSearch = (query: string) => {\n const { apiGatewayUrl } = useBrainyRich();\n const trimmed = query.trim();\n return useQuery({\n queryKey: ['brainyrich', 'tag-search', trimmed],\n enabled: trimmed.length > 0,\n queryFn: async () => {\n const data = await wspaceFetch<{ searchTags: UniversalTag[] }>(\n `query BraSearchTags($query: String!, $limit: Int) {\n searchTags(query: $query, limit: $limit) { ${TAG_FIELDS} }\n }`,\n { query: trimmed, limit: 8 },\n apiGatewayUrl\n );\n return data.searchTags;\n },\n });\n};\n\n/** Resolve (creating on first use) the workspace-scoped \"brainyrich\" vocabulary. */\nconst resolveVocabularyId = async (baseUrl?: string): Promise<string> => {\n const existing = await wspaceFetch<{ getVocabularyByKey: { id: string } | null }>(\n `query BraGetVocabulary($key: String!) {\n getVocabularyByKey(key: $key) { id }\n }`,\n { key: BRAINYRICH_VOCABULARY_KEY },\n baseUrl\n );\n if (existing.getVocabularyByKey?.id) return existing.getVocabularyByKey.id;\n\n const created = await wspaceMutate<{ createVocabulary: { id: string } }>(\n `mutation BraCreateVocabulary($input: CreateVocabularyInput!) {\n createVocabulary(input: $input) { id }\n }`,\n {\n input: {\n key: BRAINYRICH_VOCABULARY_KEY,\n name: 'BrainyRich',\n description: 'Tags for BrainyRich households, portfolios and goals.',\n allowCustomTags: true,\n },\n },\n baseUrl\n );\n return created.createVocabulary.id;\n};\n\nconst useAssignTag = (entityType: string, entityId: string) => {\n const { apiGatewayUrl } = useBrainyRich();\n const qc = useQueryClient();\n return useMutation({\n mutationFn: async (tagId: string) => {\n const data = await wspaceMutate<{ assignTag: { id: string } }>(\n `mutation BraAssignTag($input: AssignTagInput!) {\n assignTag(input: $input) { id }\n }`,\n { input: { entityType, entityId, tagId } },\n apiGatewayUrl\n );\n return data.assignTag;\n },\n onSuccess: () => qc.invalidateQueries({ queryKey: entityTagsKey(entityType, entityId) }),\n });\n};\n\nconst useCreateAndAssignTag = (entityType: string, entityId: string) => {\n const { apiGatewayUrl } = useBrainyRich();\n const qc = useQueryClient();\n return useMutation({\n mutationFn: async (label: string) => {\n const trimmed = label.trim();\n if (!trimmed) throw new Error('A tag label is required.');\n const vocabularyId = await resolveVocabularyId(apiGatewayUrl);\n // Unique key avoids collisions inside the vocabulary; the label is the display text.\n const key = `${slugify(trimmed)}-${Math.random().toString(36).slice(2, 6)}`;\n const created = await wspaceMutate<{ createTag: { id: string } }>(\n `mutation BraCreateTag($input: CreateTagInput!) {\n createTag(input: $input) { id }\n }`,\n { input: { vocabularyId, key, label: trimmed } },\n apiGatewayUrl\n );\n await wspaceMutate<{ assignTag: { id: string } }>(\n `mutation BraAssignCreatedTag($input: AssignTagInput!) {\n assignTag(input: $input) { id }\n }`,\n { input: { entityType, entityId, tagId: created.createTag.id } },\n apiGatewayUrl\n );\n return created.createTag;\n },\n onSuccess: () => qc.invalidateQueries({ queryKey: entityTagsKey(entityType, entityId) }),\n });\n};\n\nconst useRemoveTagAssignment = (entityType: string, entityId: string) => {\n const { apiGatewayUrl } = useBrainyRich();\n const qc = useQueryClient();\n return useMutation({\n mutationFn: async (assignmentId: string) => {\n const data = await wspaceMutate<{ removeTagAssignment: boolean }>(\n `mutation BraRemoveTagAssignment($id: ID!) { removeTagAssignment(id: $id) }`,\n { id: assignmentId },\n apiGatewayUrl\n );\n return data.removeTagAssignment;\n },\n onSuccess: () => qc.invalidateQueries({ queryKey: entityTagsKey(entityType, entityId) }),\n });\n};\n\n// ============================================================================\n// Component\n// ============================================================================\n\nexport interface TagFieldProps {\n /** Polymorphic entity type, e.g. \"brainyrich-household\". */\n entityType: string;\n /** The bra entity's id (an ID scalar). */\n entityId: string;\n /** Optional field label (hidden visually when `hideLabel`). */\n label?: string;\n hideLabel?: boolean;\n}\n\nexport function TagField({\n entityType,\n entityId,\n label = 'Tags',\n hideLabel = false,\n}: TagFieldProps) {\n const [draft, setDraft] = useState('');\n const assignments = useEntityTags(entityType, entityId);\n const search = useTagSearch(draft);\n const assign = useAssignTag(entityType, entityId);\n const createAndAssign = useCreateAndAssignTag(entityType, entityId);\n const remove = useRemoveTagAssignment(entityType, entityId);\n\n const busy = assign.isPending || createAndAssign.isPending;\n const assigned = assignments.data ?? [];\n const assignedTagIds = new Set(assigned.map((a) => a.tag.id));\n const suggestions = (search.data ?? []).filter((t) => !assignedTagIds.has(t.id));\n const trimmed = draft.trim();\n const exactExists = suggestions.some((t) => t.label.toLowerCase() === trimmed.toLowerCase());\n\n const inputId = `tagfield-${entityType}-${entityId}`;\n\n const commitCreate = async () => {\n if (!trimmed || busy) return;\n await createAndAssign.mutateAsync(trimmed);\n setDraft('');\n };\n\n const commitAssign = async (tagId: string) => {\n if (busy) return;\n await assign.mutateAsync(tagId);\n setDraft('');\n };\n\n const onKeyDown = (event: KeyboardEvent<HTMLInputElement>) => {\n if (event.key === 'Enter') {\n event.preventDefault();\n void commitCreate();\n }\n };\n\n return (\n <div className=\"space-y-2\">\n {!hideLabel && (\n <Label htmlFor={inputId} className=\"text-text-primary\">\n {label}\n </Label>\n )}\n\n {/* Current assignments */}\n {assignments.isLoading ? (\n <div className=\"flex flex-wrap gap-2\" aria-busy=\"true\">\n <Skeleton className=\"h-6 w-16 rounded-full\" />\n <Skeleton className=\"h-6 w-20 rounded-full\" />\n <Skeleton className=\"h-6 w-12 rounded-full\" />\n </div>\n ) : assignments.isError ? (\n <p className=\"text-sm text-status-error-text\">\n Could not load tags.{' '}\n <button\n type=\"button\"\n className=\"underline underline-offset-2\"\n onClick={() => assignments.refetch()}\n >\n Retry\n </button>\n </p>\n ) : assigned.length === 0 ? (\n <p className=\"flex items-center gap-1.5 text-sm text-text-secondary\">\n <TagIcon className=\"size-3.5\" aria-hidden=\"true\" />\n No tags yet — add one below.\n </p>\n ) : (\n <div className=\"flex flex-wrap gap-2\">\n {assigned.map((a) => (\n <Badge key={a.id} variant=\"secondary\" className=\"gap-1 pr-1\">\n <span>{a.tag.label}</span>\n <button\n type=\"button\"\n aria-label={`Remove tag ${a.tag.label}`}\n className=\"rounded-full p-0.5 text-text-secondary hover:bg-bg-sunken hover:text-text-primary focus-visible:outline focus-visible:outline-2 focus-visible:outline-border-focus disabled:opacity-50\"\n disabled={remove.isPending}\n onClick={() => remove.mutate(a.id)}\n >\n <X className=\"size-3\" aria-hidden=\"true\" />\n </button>\n </Badge>\n ))}\n </div>\n )}\n\n {/* Add control */}\n <div className=\"relative\">\n <div className=\"flex gap-2\">\n <Input\n id={inputId}\n value={draft}\n onChange={(e) => setDraft(e.target.value)}\n onKeyDown={onKeyDown}\n placeholder=\"Search or create a tag…\"\n aria-label=\"Search or create a tag\"\n autoComplete=\"off\"\n disabled={busy}\n />\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={commitCreate}\n disabled={!trimmed || busy || exactExists}\n >\n <Plus className=\"size-4\" aria-hidden=\"true\" />\n Create\n </Button>\n </div>\n\n {/* Typeahead suggestions */}\n {trimmed.length > 0 && (suggestions.length > 0 || search.isFetching) && (\n <ul\n className=\"absolute z-10 mt-1 w-full overflow-hidden rounded-md border border-border-subtle bg-bg-surface shadow-md\"\n role=\"listbox\"\n aria-label=\"Tag suggestions\"\n >\n {search.isFetching && suggestions.length === 0 ? (\n <li className=\"px-3 py-2 text-sm text-text-secondary\">Searching…</li>\n ) : (\n suggestions.map((t) => (\n <li key={t.id} role=\"option\" aria-selected=\"false\">\n <button\n type=\"button\"\n className=\"flex w-full items-center gap-2 px-3 py-2 text-left text-sm text-text-primary hover:bg-bg-sunken focus-visible:bg-bg-sunken focus-visible:outline-none disabled:opacity-50\"\n disabled={busy}\n onClick={() => commitAssign(t.id)}\n >\n <TagIcon className=\"size-3.5 text-text-secondary\" aria-hidden=\"true\" />\n {t.label}\n </button>\n </li>\n ))\n )}\n </ul>\n )}\n </div>\n\n {(assign.isError || createAndAssign.isError || remove.isError) && (\n <p className=\"text-sm text-status-error-text\">Could not update tags. Please try again.</p>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"transport.js","names":[],"sources":["../../../../src/brainyrich/components/reuse/transport.ts"],"sourcesContent":["/**\n * Shared workspace-gateway transport for the BrainyRich REUSE components.\n * @module brainyrich/components/reuse/transport\n *\n * The four reuse components (TagField, DocumentsPanel, CommentThread,\n * ReviewReminders) consume PLATFORM subgraphs — `wspace-tags-svc`,\n * `wspace-files-svc`, `wspace-conversations-svc`, `wspace-calendar-svc` —\n * that are composed into the SAME workspace supergraph the BrainyRich MFE\n * already talks to. So every call routes through the identical transport the\n * `bra*` hooks use: `graphqlFetch({ gateway: 'workspace', workspaceToken: true })`.\n *\n * This file only contains the transport primitive; each component owns its own\n * react-query hooks (kept co-located so a page can drop one component into a\n * Sheet/tab without pulling in the others).\n */\n\nimport { graphqlFetch } from '@burdenoff/fe-libs/shared/graphql';\n\n/**\n * fe-libs `graphqlFetch` coalesces `query` responses in a short-lived in-memory\n * cache keyed by operationName+variables (~15s); mutations are never cached. A\n * read issued right after a write (the react-query refetch our `invalidateQueries`\n * triggers) would otherwise return the PRE-mutation response until the TTL lapses,\n * hiding just-created rows. fe-libs' documented remedy is that write-performing\n * callers clear those caches — we do exactly that after every reuse mutation.\n */\ntype GraphqlReadCacheGlobals = {\n __graphqlFetchResponseCache?: { clear: () => void };\n __graphqlFetchPendingResponses?: { clear: () => void };\n __boffGraphQLResponseCache?: { clear: () => void };\n};\n\nconst clearGraphqlReadCaches = (): void => {\n const g = globalThis as GraphqlReadCacheGlobals;\n g.__graphqlFetchResponseCache?.clear();\n g.__graphqlFetchPendingResponses?.clear();\n g.__boffGraphQLResponseCache?.clear();\n};\n\n/** Execute a workspace-gateway GraphQL operation with the workspace token. */\nexport const wspaceFetch = async <TData>(\n query: string,\n variables: Record<string, unknown>,\n baseUrl?: string\n): Promise<TData> => {\n const response = await graphqlFetch<TData>({\n gateway: 'workspace',\n baseUrl,\n query,\n variables,\n workspaceToken: true,\n });\n\n if (response.errors?.length) {\n throw new Error(response.errors[0]?.message ?? 'GraphQL request failed');\n }\n if (!response.data) {\n throw new Error('GraphQL response did not include data');\n }\n return response.data;\n};\n\n/** Mutation transport — identical to `wspaceFetch` but clears read caches after. */\nexport const wspaceMutate = async <TData>(\n query: string,\n variables: Record<string, unknown>,\n baseUrl?: string\n): Promise<TData> => {\n const data = await wspaceFetch<TData>(query, variables, baseUrl);\n clearGraphqlReadCaches();\n return data;\n};\n"],"mappings":";;AAgCA,IAAM,UAAqC;CACzC,IAAM,IAAI;AAGV,CAFA,EAAE,6BAA6B,OAAO,EACtC,EAAE,gCAAgC,OAAO,EACzC,EAAE,4BAA4B,OAAO;GAI1B,IAAc,OACzB,GACA,GACA,MACmB;CACnB,IAAM,IAAW,MAAM,EAAoB;EACzC,SAAS;EACT;EACA;EACA;EACA,gBAAgB;EACjB,CAAC;AAEF,KAAI,EAAS,QAAQ,OACnB,OAAU,MAAM,EAAS,OAAO,IAAI,WAAW,yBAAyB;AAE1E,KAAI,CAAC,EAAS,KACZ,OAAU,MAAM,wCAAwC;AAE1D,QAAO,EAAS;GAIL,IAAe,OAC1B,GACA,GACA,MACmB;CACnB,IAAM,IAAO,MAAM,EAAmB,GAAO,GAAW,EAAQ;AAEhE,QADA,GAAwB,EACjB"}
1
+ {"version":3,"file":"transport.js","names":[],"sources":["../../../../src/brainyrich/components/reuse/transport.ts"],"sourcesContent":["/**\n * Shared workspace-gateway transport for the BrainyRich REUSE components.\n * @module brainyrich/components/reuse/transport\n *\n * The four reuse components (TagField, DocumentsPanel, CommentThread,\n * ReviewReminders) consume PLATFORM subgraphs — `wspace-tags-svc`,\n * `wspace-files-svc`, `wspace-conversations-svc`, `wspace-calendar-svc` —\n * that are composed into the SAME workspace supergraph the BrainyRich MFE\n * already talks to. So every call routes through the identical transport the\n * `bra*` hooks use: `graphqlFetch({ gateway: 'workspace', workspaceToken: true })`.\n *\n * This file only contains the transport primitive; each component owns its own\n * react-query hooks (kept co-located so a page can drop one component into a\n * Sheet/tab without pulling in the others).\n */\n\nimport { graphqlFetch } from '@burdenoff/fe-libs/shared/graphql';\n\n/**\n * fe-libs `graphqlFetch` coalesces `query` responses in a short-lived in-memory\n * cache keyed by operationName+variables (~15s); mutations are never cached. A\n * read issued right after a write (the react-query refetch our `invalidateQueries`\n * triggers) would otherwise return the PRE-mutation response until the TTL lapses,\n * hiding just-created rows. fe-libs' documented remedy is that write-performing\n * callers clear those caches — we do exactly that after every reuse mutation.\n */\ntype GraphqlReadCacheGlobals = {\n __graphqlFetchResponseCache?: { clear: () => void };\n __graphqlFetchPendingResponses?: { clear: () => void };\n __boffGraphQLResponseCache?: { clear: () => void };\n};\n\nconst clearGraphqlReadCaches = (): void => {\n const g = globalThis as GraphqlReadCacheGlobals;\n g.__graphqlFetchResponseCache?.clear();\n g.__graphqlFetchPendingResponses?.clear();\n g.__boffGraphQLResponseCache?.clear();\n};\n\n/** Execute a workspace-gateway GraphQL operation with the workspace token. */\nexport const wspaceFetch = async <TData>(\n query: string,\n variables: Record<string, unknown>,\n baseUrl?: string\n): Promise<TData> => {\n const response = await graphqlFetch<TData>({\n gateway: 'workspace',\n baseUrl,\n query,\n variables,\n workspaceToken: true,\n });\n\n if (response.errors?.length) {\n throw new Error(response.errors[0]?.message ?? 'GraphQL request failed');\n }\n if (!response.data) {\n throw new Error('GraphQL response did not include data');\n }\n return response.data;\n};\n\n/** Mutation transport — identical to `wspaceFetch` but clears read caches after. */\nexport const wspaceMutate = async <TData>(\n query: string,\n variables: Record<string, unknown>,\n baseUrl?: string\n): Promise<TData> => {\n const data = await wspaceFetch<TData>(query, variables, baseUrl);\n clearGraphqlReadCaches();\n return data;\n};\n"],"mappings":";;AAgCA,IAAM,UAAqC;CACzC,IAAM,IAAI;CAGV,AAFA,EAAE,6BAA6B,MAAM,GACrC,EAAE,gCAAgC,MAAM,GACxC,EAAE,4BAA4B,MAAM;AACtC,GAGa,IAAc,OACzB,GACA,GACA,MACmB;CACnB,IAAM,IAAW,MAAM,EAAoB;EACzC,SAAS;EACT;EACA;EACA;EACA,gBAAgB;CAClB,CAAC;CAED,IAAI,EAAS,QAAQ,QACnB,MAAU,MAAM,EAAS,OAAO,IAAI,WAAW,wBAAwB;CAEzE,IAAI,CAAC,EAAS,MACZ,MAAU,MAAM,uCAAuC;CAEzD,OAAO,EAAS;AAClB,GAGa,IAAe,OAC1B,GACA,GACA,MACmB;CACnB,IAAM,IAAO,MAAM,EAAmB,GAAO,GAAW,CAAO;CAE/D,OADA,EAAuB,GAChB;AACT"}
@@ -1,5 +1,4 @@
1
1
  import { ReactNode } from 'react';
2
-
3
2
  export declare function useReducedMotion(): boolean;
4
3
  declare const CORE_TOKENS: {
5
4
  readonly brand: "--color-action-primary-bg";